Declare Resolvers, Generate the Schema
GraphQL — Code-First
In code-first GraphQL, you write TypeScript classes and resolvers and Nest generates the SDL. Decorators describe types just like REST DTOs.
What you'll learn
- Install @nestjs/graphql and the Apollo driver
- Configure GraphQLModule
- Write resolvers with @ObjectType / @Query / @Mutation
There are two ways to do GraphQL in Nest. Schema-first starts with a
.graphql file. Code-first starts with TypeScript classes and
generates the schema from them. This lesson covers code-first — it’s
the more popular choice in Nest projects.
Install
npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql Configure GraphQLModule
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'schema.gql',
playground: false,
sortSchema: true,
}),
],
})
export class AppModule {} autoSchemaFile writes the generated SDL to disk so you can review it in
PRs. Set it to true to keep the schema in memory only.
ObjectTypes and Resolvers
Decorate plain classes to expose them as GraphQL types. Resolvers are
where @Query and @Mutation handlers live.
import {
ObjectType, Field, ID,
Resolver, Query, Mutation, Args, Int,
} from '@nestjs/graphql';
@ObjectType()
export class User {
@Field(() => ID) id: number;
@Field() email: string;
@Field({ nullable: true }) name?: string;
}
@Resolver(() => User)
export class UsersResolver {
constructor(private readonly users: UsersService) {}
@Query(() => [User])
users() {
return this.users.findAll();
}
@Query(() => User, { nullable: true })
user(@Args('id', { type: () => Int }) id: number) {
return this.users.findById(id);
}
@Mutation(() => User)
createUser(@Args('email') email: string, @Args('name') name: string) {
return this.users.create(email, name);
}
} Notice how @Field(() => ID) and @Args('id', { type: () => Int })
spell out GraphQL scalar types — TypeScript’s number is ambiguous to
the schema generator, so you have to be explicit.
Resolving Relations
For nested fields that aren’t on the entity, write a @ResolveField. It
runs only when the client actually asks for that field.
@ResolveField(() => [Post])
posts(@Parent() user: User) {
return this.posts.findByUser(user.id);
} This is also where the DataLoader pattern lives — batch resolves
together to avoid N+1 queries when many users ask for their posts in one
request. @nestjs/dataloader (community) wraps it nicely.
When to Pick GraphQL
GraphQL is great when you have many clients (web, mobile, third-party) each wanting different shapes of the same data. For a single first-party frontend, plain REST + OpenAPI types is usually less ceremony.
Microservices — TCP, Redis, NATS →