NestJS Logo

CLI Plugin

Warning This chapter applies only to the code first approach.

TypeScript's metadata reflection system has several limitations that make it impossible to, for instance, determine which properties a class consists of, or whether a given property is optional or required. However, some of these constraints can be addressed at compile time. Nest provides a plugin that enhances the TypeScript compilation process to reduce the amount of boilerplate code required.

Hint This plugin is opt-in. If you prefer, you can declare all decorators manually, or only specific decorators where you need them.

Overview#

The GraphQL plugin automatically does the following:

  • annotate all properties of input object, object type, and args classes with @Field, unless @HideField is used
  • set the nullable property depending on the question mark (e.g., name?: string sets nullable: true)
  • set the type property depending on the type (arrays are supported as well)
  • generate descriptions for properties based on comments (if introspectComments is set to true)

Your filenames must have one of the following suffixes to be analyzed by the plugin: ['.input.ts', '.args.ts', '.entity.ts', '.model.ts'] (e.g., author.entity.ts). If you use a different suffix, adjust the plugin's behavior with the typeFileNameSuffix option (see below).

With what we've covered so far, you have to duplicate a lot of code to tell the package how your type should be declared in GraphQL. For example, you could define an Author class as follows:

authors/models/author.model.ts
JS TS

@ObjectType()
export class Author {
  @Field(type => ID)
  id: number;

  @Field({ nullable: true })
  firstName?: string;

  @Field({ nullable: true })
  lastName?: string;

  @Field(type => [Post])
  posts: Post[];
}

While this isn't a significant issue in medium-sized projects, it becomes verbose and hard to maintain once you have a large set of classes.

With the GraphQL plugin enabled, the class definition above can be declared as follows:

authors/models/author.model.ts
JS TS

@ObjectType()
export class Author {
  @Field(type => ID)
  id: number;
  firstName?: string;
  lastName?: string;
  posts: Post[];
}

The plugin adds the appropriate decorators on the fly based on the Abstract Syntax Tree, so you don't have to scatter @Field decorators throughout the code.

Hint The plugin automatically generates any missing GraphQL properties. If you need to override them, set them explicitly with @Field().

Comments introspection#

With the comments introspection feature enabled, the CLI plugin generates descriptions for fields based on comments.

For example, consider a roles property:


/**
 * A list of user's roles
 */
@Field(() => [String], {
  description: `A list of user's roles`
})
roles: string[];

Here, you must duplicate the description. With introspectComments enabled, the CLI plugin extracts these comments and automatically provides descriptions for properties. The field above can then be declared as follows:


/**
 * A list of user's roles
 */
roles: string[];

Using the CLI plugin#

To enable the plugin, open nest-cli.json (if you use the Nest CLI) and add the following plugins configuration:


{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": ["@nestjs/graphql"]
  }
}

Use the options property to customize the behavior of the plugin:


{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": [
      {
        "name": "@nestjs/graphql",
        "options": {
          "typeFileNameSuffix": [".input.ts", ".args.ts"],
          "introspectComments": true
        }
      }
    ]
  }
}

The options property accepts the following options:


export interface PluginOptions {
  typeFileNameSuffix?: string | string[];
  introspectComments?: boolean;
  esmCompatible?: boolean;
  debug?: boolean;
}
OptionDefaultDescription
typeFileNameSuffix['.input.ts', '.args.ts', '.entity.ts', '.model.ts']Suffixes of the files that contain GraphQL types
introspectCommentsfalseIf set to true, the plugin generates descriptions for properties based on comments
esmCompatibleInferred from each file's module formatIf set to true, the plugin emits ESM-compatible code (no require() calls, and output extensions appended to relative imports).
debugfalseIf set to true, the plugin logs diagnostic messages (e.g., when it finds a type that isn't exported)

If you don't use the CLI and have a custom webpack configuration instead, you can use this plugin in combination with ts-loader:


getCustomTransformers: (program) => ({
  before: [require('@nestjs/graphql/plugin').before({}, program)]
}),

SWC builder#

For standard (non-monorepo) setups, using CLI plugins with the SWC builder requires type checking to be enabled, as described in SWC type checking.


$ nest start -b swc --type-check

For monorepo setups, follow the instructions in monorepo and CLI plugins.


$ npx ts-node src/generate-metadata.ts
# OR npx ts-node apps/{YOUR_APP}/src/generate-metadata.ts

Then, load the serialized metadata file in the GraphQLModule configuration, as shown below:


import metadata from './metadata.js'; // <-- file auto-generated by the "PluginMetadataGenerator"

GraphQLModule.forRoot<...>({
  ..., // other options
  metadata,
}),

Integration with ts-jest (e2e tests)#

When you run e2e tests with this plugin enabled, you may run into issues compiling the schema. For example, one of the most common errors is:


Object type <name> must define one or more fields.

This happens because the jest configuration doesn't import the @nestjs/graphql/plugin plugin anywhere.

To fix this, create the following file in your e2e tests directory:


const transformer = require('@nestjs/graphql/plugin');

module.exports.name = 'nestjs-graphql-transformer';
// you should change the version number anytime you change the configuration below - otherwise, jest will not detect changes
module.exports.version = 1;

module.exports.factory = (cs) => {
  return transformer.before(
    {
      // @nestjs/graphql/plugin options (can be empty)
    },
    cs.program, // "cs.tsCompiler.program" for older versions of Jest (<= v27)
  );
};

With this in place, import the AST transformer in your jest configuration file. By default (in the starter application), the e2e test configuration file is located in the test folder and is named jest-e2e.json.


{
  ... // other configuration
  "globals": {
    "ts-jest": {
      "astTransformers": {
        "before": ["<path to the file created above>"]
      }
    }
  }
}

If you use jest@^29 or later, use the snippet below instead, as the previous approach is deprecated.


{
  ... // other configuration
  "transform": {
    "^.+\\.(t|j)s$": [
      "ts-jest",
      {
        "astTransformers": {
          "before": ["<path to the file created above>"]
        }
      }
    ]
  }
}
Edit on GitHub

Support us

Nest is an MIT-licensed open source project. It can grow thanks to the support of these awesome people. If you'd like to join them, please read more here.

Principal Sponsors

SerpApi LogoTrilon LogoMojam Logo

Sponsors / Partners

Become a sponsor