GraphQL with NestJS
Integrating GraphQL into a NestJS application using Apollo Server or other GraphQL libraries.
Setting up Apollo Server with NestJS
Explanation: Setting up Apollo Server with NestJS
This guide will walk you through integrating Apollo Server into a NestJS application to create a GraphQL API. NestJS provides a structured and modular way to build server-side applications, and Apollo Server handles the GraphQL layer.
The core idea is to leverage NestJS modules and dependency injection to manage your GraphQL schema, resolvers, and Apollo Server instance. We'll use the @nestjs/graphql
package to simplify this integration.
Here's a high-level overview of the process:
- Install Dependencies: Install the necessary npm packages for Apollo Server and NestJS GraphQL integration.
- Define Schema: Create your GraphQL schema using either code-first or schema-first approaches.
- Implement Resolvers: Write resolvers that fetch and transform data to fulfill GraphQL queries.
- Configure Apollo Server: Configure Apollo Server within your NestJS application using the
GraphQLModule
. - Integrate Playground (Optional): Enable the Apollo Server Playground for easy API exploration.
Detailed Guide: Installing and Configuring Apollo Server within a NestJS Application
Step 1: Install Dependencies
First, install the required packages using npm or yarn:
npm install @nestjs/graphql @nestjs/platform-express graphql apollo-server-express class-validator class-transformer
Or with yarn:
yarn add @nestjs/graphql @nestjs/platform-express graphql apollo-server-express class-validator class-transformer
Explanation:
@nestjs/graphql
: The official NestJS GraphQL module that simplifies integration with Apollo Server.@nestjs/platform-express
: Provides the Express platform adapter for NestJS.graphql
: The core GraphQL library.apollo-server-express
: Allows you to use Apollo Server with Express.class-validator
&class-transformer
: Needed for input validation in GraphQL resolvers (optional, but highly recommended).
Step 2: Define Schema (Code-First Approach - Recommended)
The code-first approach allows you to define your schema using TypeScript classes.
Create a file named src/entities/author.entity.ts
:
import { ObjectType, Field, ID } from '@nestjs/graphql';
@ObjectType()
export class Author {
@Field(() => ID)
id: string;
@Field()
firstName: string;
@Field()
lastName: string;
}
Create a file named src/entities/book.entity.ts
:
import { ObjectType, Field, ID } from '@nestjs/graphql';
import { Author } from './author.entity';
@ObjectType()
export class Book {
@Field(() => ID)
id: string;
@Field()
title: string;
@Field(() => Author)
author: Author;
}
Step 3: Implement Resolvers
Create a resolver file named src/book/book.resolver.ts
:
import { Resolver, Query, Args, Int, ResolveField, Parent } from '@nestjs/graphql';
import { Book } from '../entities/book.entity';
import { Author } from '../entities/author.entity';
@Resolver(() => Book)
export class BookResolver {
private books: Book[] = [
{ id: '1', title: 'The Lord of the Rings', author: { id: '1', firstName: 'J.R.R', lastName: 'Tolkien' } },
{ id: '2', title: 'The Hobbit', author: { id: '1', firstName: 'J.R.R', lastName: 'Tolkien' } },
{ id: '3', title: 'The Hitchhiker\'s Guide to the Galaxy', author: { id: '2', firstName: 'Douglas', lastName: 'Adams' } },
];
private authors: Author[] = [
{ id: '1', firstName: 'J.R.R', lastName: 'Tolkien' },
{ id: '2', firstName: 'Douglas', lastName: 'Adams' },
];
@Query(() => [Book])
async books(): Promise {
return this.books;
}
@Query(() => Book, { nullable: true })
async book(@Args('id', { type: () => Int }) id: number): Promise {
return this.books.find(book => book.id === id.toString());
}
@ResolveField(() => Author)
async author(@Parent() book: Book): Promise {
return this.authors.find(author => author.id === book.author.id);
}
}
Create a resolver file named src/author/author.resolver.ts
:
import { Resolver, Query, Args, Int, ResolveField, Parent } from '@nestjs/graphql';
import { Author } from '../entities/author.entity';
@Resolver(() => Author)
export class AuthorResolver {
private authors: Author[] = [
{ id: '1', firstName: 'J.R.R', lastName: 'Tolkien' },
{ id: '2', firstName: 'Douglas', lastName: 'Adams' },
];
@Query(() => [Author])
async authors(): Promise {
return this.authors;
}
@Query(() => Author, { nullable: true })
async author(@Args('id', { type: () => Int }) id: number): Promise {
return this.authors.find(author => author.id === id.toString());
}
}
Explanation:
@Resolver(() => Book)
: Indicates that this class is a resolver for theBook
type.@Query(() => [Book])
: Defines a GraphQL query namedbooks
that returns an array ofBook
objects.@Query(() => Book, { nullable: true })
: Defines a GraphQL query namedbook
which takes an ID as an argument and returns a single Book object.@ResolveField(() => Author)
: Defines a resolver for theauthor
field within theBook
type, allowing you to fetch the author related to a particular book.@Args
: Extracts arguments passed to the query.@Parent()
: In@ResolveField
,@Parent()
injects the parent object (in this case, aBook
instance) into the resolver function, which allows you to access the parent's properties and use them to fetch related data.
Step 4: Configure Apollo Server with GraphQLModule
Modify your src/app.module.ts
file to configure the GraphQLModule
:
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { BookResolver } from './book/book.resolver';
import { AuthorResolver } from './author/author.resolver';
@Module({
imports: [
GraphQLModule.forRoot({
driver: ApolloDriver,
autoSchemaFile: 'schema.gql', // Generates the schema file.
playground: true, // Enable GraphQL Playground in development.
debug: true, //Enable Debug
}),
],
providers: [BookResolver, AuthorResolver],
})
export class AppModule {}
Explanation:
GraphQLModule.forRoot()
: Configures theGraphQLModule
with the provided options.driver: ApolloDriver
: Specifies to use Apollo Server as the GraphQL driver.autoSchemaFile: 'schema.gql'
: Automatically generates the GraphQL schema file. This is crucial for the code-first approach. The file will be located in the root of your project after the application is run for the first time.playground: true
: Enables the Apollo Server Playground for easy testing. Access it in your browser (usually at/graphql
).debug: true
: Enables debug to show error message
Step 5: Run the Application
Start your NestJS application:
npm run start:dev
Open your browser and navigate to http://localhost:3000/graphql
(or the port your application is running on) to access the Apollo Server Playground. You should see the GraphQL interface where you can explore your schema and execute queries.
Step 6: Create module for the resolvers (Optional but recomended)
Create new module named book to isolate the book and the authoe resolver
nest g module book
Update the new book.module.ts with the author and book resolver
import { Module } from '@nestjs/common';
import { BookResolver } from './book.resolver';
import { AuthorResolver } from 'src/author/author.resolver';
@Module({
providers: [BookResolver,AuthorResolver]
})
export class BookModule {}
Now Import the Book module in the app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { BookModule } from './book/book.module';
@Module({
imports: [
GraphQLModule.forRoot({
driver: ApolloDriver,
autoSchemaFile: 'schema.gql', // Generates the schema file.
playground: true, // Enable GraphQL Playground in development.
debug: true, //Enable Debug
}),
BookModule,
],
providers: [],
})
export class AppModule {}