# Code Generation

We use code generation to automatically generate API schemas and type-safe clients for all API integrations. All generated files are ignored by git to reduce noise, make reviews easier on PRs, and only request code reviews from relevant teams.

{% hint style="warning" %}
You will need `java` installed on your machine to be able to run the `yarn codegen` command, more precisely the `openapi-generator` sub-command. Find more about the installation [here](https://github.com/OpenAPITools/openapi-generator#13---download-jar).
{% endhint %}

## Understanding how code generation works

We are only tracking file that are coming from an external source, e.g. `contentfulTypes.d.ts` which depends on Contentful to be generated. The same goes for `openapi.yaml` files from external services.

Running  `yarn codegen` will generate all the schemas and clients for the project. It can take up to 10 minutes to run. The output of each step is cached by NX and subsequent runs will usually be much faster.

In our GitHub workflows, we are caching theses generated files to avoid to re-generate them at each push. However, the generated files have to be updated when some specific files are changed (e.g. `*.resolvers.ts`, `*.dto.ts`, etc). There is a hashFiles variable in our GitHub workflows which contain a list of file patterns which may affect code generation† and are used to invalidate the cache. You should follow this naming convention for files which need to invalidate the code generation cache.

```
scripts/codegen.js
libs/cms/src/lib/generated/contentfulTypes.d.ts
apps/air-discount-scheme/web/i18n/withLocale.tsx
apps/air-discount-scheme/web/components/AppLayout/AppLayout.tsx
apps/air-discount-scheme/web/components/Header/Header.tsx
apps/air-discount-scheme/web/screens/**.tsx
libs/application/types/src/lib/ApplicationTypes.ts
apps/**/codegen.yml
libs/**/codegen.yml
apps/**/*.model.ts
libs/**/*.model.ts
apps/**/*.enum.ts
libs/**/*.enum.ts
apps/**/queries/**/*.tsx?
libs/**/queries/**/*.tsx?
apps/**/*.resolver.ts
libs/**/*.resolver.ts
apps/**/*.service.ts
libs/**/*.service.ts
apps/**/*.dto.ts
libs/**/*.dto.ts
apps/**/*.input.ts
libs/**/*.input.ts
apps/**/*.module.ts
libs/**/*.module.ts
apps/**/*.controller.ts
libs/**/*.controller.ts
```

## Code generation build targets

We have 3 different targets that can be configured inside `project.json` to generate schemas and types.

* `codegen/backend-client`
* `codegen/backend-schema`
* `codegen/frontend-client`

These are designed with smart defaults and run in the correct order thanks to the NX dependency graph.

## Generating schema and client types

If you are changing some API service, you may need to re-generate the API schema using:

```bash
yarn nx run <service-project>:codegen/backend-schema
```

With an updated API schema, you may need to re-generate client code with:

```bash
yarn nx run <client-project>:codegen/backend-client
```

If you have changed a public API schema, you may need to re-generate front-end client code with:

```bash
yarn nx run <frontend-project>:codegen/frontend-client
```

## Configuring code generation

The following guides cover how to set up code generation in your project.

### Generate OpenAPI schemas for your API project

First, you need to create an `openApi.ts` file to define the document builder. Add this file to the `src` directory of your project alongside the `index.ts`.

```typescript
import { DocumentBuilder } from '@nestjs/swagger'

export const openApi = new DocumentBuilder()
  .setTitle('title')
  .setDescription('description')
  .setVersion('version')
  .addTag('application')
  // More Swagger configuration, i.e. `.addOAuth2(...) to add 
  // authorization to test the API in the Swagger UI
  .build()
```

Next, we need to create an `buildOpenApi.ts` that will consume the previous file and generate the `openapi.yaml` file.

```typescript
import { buildOpenApi } from '@island.is/infra-nest-server'

import { AppModule } from './app/app.module'
import { openApi } from './openApi'

buildOpenApi({
  path: 'PATH/src/openapi.yaml',
  appModule: AppModule,
  openApi,
})
```

Finally, we set up a `codegen/backend-schema` script in `project.json` for the project.

```json
    "codegen/backend-schema": {
      "executor": "@nx/workspace:run-commands",
      "options": {
        "command": "yarn ts-node -P PATH/tsconfig.app.json PATH/src/buildOpenApi.ts"
      },
      "outputs": ["{projectRoot}/src/openapi.yaml"]
    }
```

If your service is running a service like redis, you will need to ignore it for running the build-open-api script like follow in the `project.json`

```json
  "command": "cross-env INIT_SCHEMA=true yarn ts-node ..."
```

and in the module where the redis manager is defined

```typescript
if (process.env.INIT_SCHEMA === 'true') {
  CacheModule = NestCacheModule.register()
} else {
  CacheModule = NestCacheModule.register({
    store: redisStore,
    redisInstance: createNestJSCache({...}),
  })
}
```

### Generate GraphQL schema for your API project

This is similar to generating OpenAPI schemas. Configure your app module to generate a schema file at startup like this:

```typescript
const debug = process.env.NODE_ENV === 'development'
const playground = debug || process.env.GQL_PLAYGROUND_ENABLED === 'true'
const autoSchemaFile = environment.production
  ? true
  : 'APP-PATH/src/api.graphql'

@Module({
  imports: [
    GraphQLModule.forRoot({
      debug,
      playground,
      autoSchemaFile,
      // ...
    }),
    // ...
  ],
})
export class AppModule {}
```

For the `codegen/backend-schema` build target, you can use the build-graphql-schema.ts utility to load your AppModule and generate the GraphQL schema:

```json
    "codegen/backend-schema": {
      "executor": "nx:run-commands",
      "options": {
        "command": "yarn ts-node -P PATH/tsconfig.json scripts/build-graphql-schema.ts PATH/src/app/app.module"
      },
      "outputs": ["{projectRoot}/src/api.graphql"]
    },
```

### Generate an OpenAPI client for a monorepo API

You can use [OpenAPI Generator](https://openapi-generator.tech/) and the OpenAPI schema generated above to generate a type-safe client library to integrate with it. Just create a client project and add the following target to the `project.json`.

```json
    "codegen/backend-client": {
      "executor": "@nx/workspace:run-commands",
      "options": {
        "command": "yarn openapi-generator -o PATH/gen/fetch -i OTHER-PROJECT/src/openapi.yaml"
      },
      "outputs": ["{projectRoot}/gen/fetch"],
    }
```

You'll also need to add an implicit dependency from the client project to the API project (also in `project.json`. This is to make sure NX first runs the schema target of the API project before running the client target of the client project.

```
  "implicitDependencies": ["OTHER-PROJECT-NAME"]
```

### Generate an OpenAPI client for an external REST API

Creating client libraries for external APIs is similar. You should ask the API provider for an OpenAPI schema and add it to our monorepo in your client project. Name it something like `clientConfig.json` (don't name it `openapi.yaml` which is ignored by git). Then add a `codegen/backend-client` build target `project.json`:

```json
    "codegen/backend-client": {
      "executor": "@nx/workspace:run-commands",
      "options": {
        "command": "yarn openapi-generator -o PATH/gen/fetch -i OTHER-PROJECT/src/openapi.yaml"
      },
      "outputs": ["{projectRoot}/gen/fetch"],
    }
```

In many cases you can download the OpenAPI schema directly from running servers. In that case it is handy to define a `update-openapi-document` build target in the client project. Something like this:

```bash
    "update-openapi-document": {
      "executor": "nx:run-commands",
      "options": {
        "command": "curl API-URL/swagger.json > PROJECT-PATH/src/clientConfig.json",
      }
    },
```

In case the API is on X-Road / Straumurinn and you're running [xroad proxy](/development/getting-started.md#running-proxy-against-development-service), you can fetch OpenAPI schemas for REST APIs with a build target like this:

```json
    "update-openapi-document": {
      "executor": "nx:run-commands",
      "options": {
        "curl -H \"X-Road-Client: IS-DEV/GOV/10000/island-is-client\" http://localhost:8081/r1/XROAD-SERVICE-PATH/getOpenAPI?serviceCode=SERVICE-CODE > PROJECT-PATH/src/clientConfig.json",
      }
    },
```

### Generate GraphQL types and hooks&#x20;

You can use [GraphQL Code Generator](https://the-guild.dev/graphql/codegen)  to generate all kinds of TypeScript types and even React hooks to integrate with GraphQL APIs.

Create a `codegen.yml` file in your project:

```yml
schema:
  - PATH/api.graphql
generates:
  PATH/src/schema.ts:
    plugins:
      - ...
hooks:
  afterAllFileWrite:
    - prettier --write
```

Finally, you can configure a `codegen/frontend-client` inside your `project.json`

```json
"codegen/frontend-client": {
  "executor": "@nx/workspace:run-commands",
  "options": {
    "command": "graphql-codegen --config PATH/codegen.yml"
  },
  "outputs": ["PATH/src/schema.ts"]
}
```

{% hint style="info" %}
You should use one of the following names for generated files so they're ignored by git:&#x20;

* `schema.ts`
* `possibleTypes.json`
* `fragmentTypes.json`
* `*.generated.ts`
  {% endhint %}

Instead of generating one big TypeScript file in each project, you might want to use the `near-operation-file` preset. It creates small TypeScript files with hooks and operation types for operations defined in graphql files. These can be next to where they're used.

```yaml
schema:
  - PATH/api.graphql
documents:
  - UI-PATH/src/**/*.graphql
generates:
  UI-PATH/src/:
    preset: 'near-operation-file'
    presetConfig:
      baseTypesPath: '~@island.is/api/schema'
    plugins:
      - typescript-operations
      - typescript-react-apollo
hooks:
  afterAllFileWrite:
    - prettier --write
```

Make sure to configure the `codegen/frontend-client` outputs to match:

```json
"codegen/frontend-client": {
  "executor": "@nx/workspace:run-commands",
  "options": {
    "command": "graphql-codegen --config PATH/codegen.yml"
  },
  "outputs": ["PATH/src/**/*.generated.ts"]
}
```

{% hint style="info" %}
You can also use GraphQL Code Generator to integrate a GraphQL API inside a backend client project. You can explore which plugins and presets [are available](https://the-guild.dev/graphql/codegen/plugins). In the least you can use it to validate your operations against the schema, and generate TypeScript types which you then use manually, eg with [Enhanced Fetch](https://github.com/island-is/island.is/tree/main/libs/clients/middlewares).

The main thing is to configure the codegen build target as `codegen/backend-client` instead of `codegen/frontend-client`.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.devland.is/development/codegen.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
