Quick start

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. It is an elegant approach that solves many of these problems that we have with typical REST apis. There is a great comparison between GraphQL and REST. In this set of articles, we’re not going to explain what the GraphQL is, but rather show how to work with the dedicated @nestjs/graphql module. This chapter assumes that you are already familiar with GraphQL essentials.

The GraphQLModule is nothing more than a wrapper around the Apollo server. We don’t reinvent the wheel but provide a ready to use module instead, that brings a clean way to play with the GraphQL and Nest together.

Installation

Firstly, we need to install the required packages:

  1. $ npm i --save @nestjs/graphql apollo-server-express graphql-tools graphql

Overview

Nest offers two ways of building GraphQL applications, the schema first and the code first respectively.

In the schema first approach, the source of truth is a GraphQL SDL (Schema Definition Language). It’s a language-agnostic way which basically allows you to share schema files between different platforms. Furthermore, Nest will automatically generate your TypeScript definitions based on the GraphQL schemas (using either classes or interfaces) to reduce redundancy.

In the code first approach on the other hand, you’ll only use decorators and TypeScript classes to generate the corresponding GraphQL schema. It becomes very handy if you prefer to work exclusively with TypeScript and avoid the context switching between languages syntax.

Getting started

Once the packages are installed, we can register the GraphQLModule.

  1. @@filename()
  2. import { Module } from '@nestjs/common';
  3. import { GraphQLModule } from '@nestjs/graphql';
  4. @Module({
  5. imports: [
  6. GraphQLModule.forRoot({}),
  7. ],
  8. })
  9. export class ApplicationModule {}

The .forRoot() method takes an options object as an argument. These options will be passed down to the underlying Apollo instance (read more about available settings here). For instance, if you want to disable the playground and turn off the debug mode, simply pass the following options:

  1. @@filename()
  2. import { Module } from '@nestjs/common';
  3. import { GraphQLModule } from '@nestjs/graphql';
  4. @Module({
  5. imports: [
  6. GraphQLModule.forRoot({
  7. debug: false,
  8. playground: false,
  9. }),
  10. ],
  11. })
  12. export class ApplicationModule {}

As mentioned, all these settings will be forwarded to the ApolloServer constructor.

Playground

The playground is a graphical, interactive, in-browser GraphQL IDE, available by default on the same URL as the GraphQL server itself. Whilst your application is running in the background, open your web browser and navigate to http://localhost:3000/graphql (host and port may vary depending on your configuration).

Quick start - 图1

Multiple endpoints

Another useful feature of this module is a capability to serve multiple endpoints at once. Thanks to that, you can decide which modules should be included in which endpoint. By default, GraphQL searches for resolvers throughout the whole app. To limit only a subset of modules, you can use the include property.

  1. GraphQLModule.forRoot({
  2. include: [CatsModule],
  3. }),

Schema first

To start using schema first way, simply add typePaths array inside the options object.

  1. GraphQLModule.forRoot({
  2. typePaths: ['./**/*.graphql'],
  3. }),

The typePaths property indicates where the GraphQLModule should look for the GraphQL files.All those files will be eventually combined in the memory which means that you can split your schemas into several files and hold them near to their resolvers.

Separate creation of both GraphQL types and corresponding TypeScript definitions creates unnecessary redundancy. Eventually, we end up without a single source of truth and each change made within SDL forces us to adjust interfaces as well. Thus, the @nestjs/graphql package serves another interesting functionality, which is the automatic generation of TS definitions using abstract syntax tree (AST). In order to enable it, simply add definitions property.

  1. GraphQLModule.forRoot({
  2. typePaths: ['./**/*.graphql'],
  3. definitions: {
  4. path: join(process.cwd(), 'src/graphql.ts'),
  5. },
  6. }),

The src/graphql.ts indicates where to save TypeScript output. By default, all types are transformed to the interfaces. However, you can switch to classes instead by changing outputAs property to class.

  1. GraphQLModule.forRoot({
  2. typePaths: ['./**/*.graphql'],
  3. definitions: {
  4. path: join(process.cwd(), 'src/graphql.ts'),
  5. outputAs: 'class',
  6. },
  7. }),

However, generating type definitions on each application start may not be necessary. Instead, we might prefer to have full control, produce typings only when a dedicated command has been executed. In this case, we can create our own script, let’s say generate-typings.ts:

  1. import { GraphQLDefinitionsFactory } from '@nestjs/graphql';
  2. import { join } from 'path';
  3. const definitionsFactory = new GraphQLDefinitionsFactory();
  4. definitionsFactory.generate({
  5. typePaths: ['./src/**/*.graphql'],
  6. path: join(process.cwd(), 'src/graphql.ts'),
  7. outputAs: 'class',
  8. });

Afterward, simply run your file:

  1. ts-node generate-typings

info Hint You can also compile a script beforehand and use node executable instead.

In order to switch to the watch mode (automatically generate typings on any .graphql file change), pass watch option to the generate() method.

  1. definitionsFactory.generate({
  2. typePaths: ['./src/**/*.graphql'],
  3. path: join(process.cwd(), 'src/graphql.ts'),
  4. outputAs: 'class',
  5. watch: true,
  6. });

A fully working sample is available here.

Code first

In the code first approach, you’ll only use decorators and TypeScript classes to generate the corresponding GraphQL schema.

Nest is using an amazing type-graphql library under the hood in order provide this functionality. Hence, before we proceed, you have to install this package.

  1. $ npm i type-graphql

Once the installation process is completed, we can add autoSchemaFile property to the options object.

  1. GraphQLModule.forRoot({
  2. autoSchemaFile: 'schema.gql',
  3. }),

The autoSchemaFile indicates a path where your automatically generated schema will be created. Additionally, you can pass the buildSchemaOptions property - an options object which will be passed in to the buildSchema() function (from the type-graphql package).

A fully working sample is available here.

Async configuration

Quite often you might want to asynchronously pass your module options instead of passing them beforehand. In such case, use forRootAsync() method, that provides a couple of various ways to deal with async data.

First possible approach is to use a factory function:

  1. GraphQLModule.forRootAsync({
  2. useFactory: () => ({
  3. typePaths: ['./**/*.graphql'],
  4. }),
  5. }),

Obviously, our factory behaves like every other one (might be async and is able to inject dependencies through inject).

  1. GraphQLModule.forRootAsync({
  2. imports: [ConfigModule],
  3. useFactory: async (configService: ConfigService) => ({
  4. typePaths: configService.getString('GRAPHQL_TYPE_PATHS'),
  5. }),
  6. inject: [ConfigService],
  7. }),

Alternatively, you are able to use class instead of a factory.

  1. GraphQLModule.forRootAsync({
  2. useClass: GqlConfigService,
  3. }),

Above construction will instantiate GqlConfigService inside GraphQLModule and will leverage it to create options object. The GqlConfigService has to implement GqlOptionsFactory interface.

  1. @Injectable()
  2. class GqlConfigService implements GqlOptionsFactory {
  3. createGqlOptions(): GqlModuleOptions {
  4. return {
  5. typePaths: ['./**/*.graphql'],
  6. };
  7. }
  8. }

In order to prevent the creation of GqlConfigService inside GraphQLModule and use a provider imported from a different module, you can use the useExisting syntax.

  1. GraphQLModule.forRootAsync({
  2. imports: [ConfigModule],
  3. useExisting: ConfigService,
  4. }),

It works the same as useClass with one critical difference - GraphQLModule will lookup imported modules to reuse already created ConfigService, instead of instantiating it on its own.