This chapter follows Resolvers and execution.
NestJS organizes server code into modules, providers, and decorators. @nestjs/graphql with the Apollo driver generates SDL from TypeScript classes—a code-first workflow. The same resolver ideas apply in Apollo Server or GraphQL Yoga; Nest adds structure and dependency injection.
Install dependencies
From an existing Nest project:
pnpm add @nestjs/graphql @nestjs/apollo @apollo/server graphqlpnpm add graphql-ws ws subscriptions-transport-wspnpm add -D @types/wsMatch major versions to your Nest release; @nestjs/graphql 14 targets current Apollo Server integrations.
Register GraphQLModule
Import GraphQLModule in AppModule:
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";import { Module } from "@nestjs/common";import { GraphQLModule } from "@nestjs/graphql";import { join } from "node:path";
@Module({ imports: [ GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: join(process.cwd(), "schema.gql"), sortSchema: true, playground: process.env.NODE_ENV !== "production", subscriptions: { "graphql-ws": true, }, context: ({ req }) => ({ req, userId: req.headers["x-user-id"] ?? null }), }), ],})export class AppModule {}autoSchemaFile writes SDL for reviews and codegen. Disable playground in production.
Define object and input types
import { Field, ID, ObjectType } from "@nestjs/graphql";
@ObjectType()export class Book { @Field(() => ID) id!: string;
@Field() title!: string;}import { Field, InputType } from "@nestjs/graphql";
@InputType()export class CreateBookInput { @Field() title!: string;}Generated SDL mirrors these definitions for clients and tools.
Queries, mutations, and field resolvers
import { Args, Mutation, Query, Resolver, ResolveField, Parent } from "@nestjs/graphql";import { BooksService } from "./books.service";import { Book } from "./dto/book.model";import { CreateBookInput } from "./dto/create-book.input";
@Resolver(() => Book)export class BooksResolver { constructor(private readonly books: BooksService) {}
@Query(() => [Book]) books() { return this.books.findAll(); }
@Mutation(() => Book) createBook(@Args("input") input: CreateBookInput) { return this.books.create(input); }
@ResolveField(() => String) slug(@Parent() book: Book) { return book.title.toLowerCase().replace(/\s+/g, "-"); }}Register the resolver in a BooksModule with BooksService as a provider.
Validation with class-validator
Enable a global validation pipe and decorate inputs:
import { ValidationPipe } from "@nestjs/common";
async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ whitelist: true })); await app.listen(3000);}import { Length } from "class-validator";
@InputType()export class CreateBookInput { @Field() @Length(1, 200) title!: string;}GraphQL validates types first; class-validator enforces domain constraints before resolvers run.
Request-scoped DataLoader
Provide a loader factory per request to batch relations:
import { Injectable, Scope } from "@nestjs/common";import DataLoader from "dataloader";import { AuthorsService } from "./authors.service";
@Injectable({ scope: Scope.REQUEST })export class AuthorsLoader { readonly byId: DataLoader<string, Author>;
constructor(authors: AuthorsService) { this.byId = new DataLoader((ids) => authors.findByIds([...ids])); }}Inject AuthorsLoader in a @ResolveField() for Book.author.
Subscriptions
Publish events from a service with @nestjs/graphql PubSub or an Redis-backed pub/sub for multi-instance deployments. Define a subscription resolver:
import { Subscription } from "@nestjs/graphql";
@Subscription(() => Book, { filter: (payload, vars) => payload.bookAdded.id === vars.id })bookAdded() { return this.pubSub.asyncIterableIterator("bookAdded");}Clients connect with graphql-ws and the same auth headers or connection params you document in the security chapter.
Next: Schema design in GraphQL.