GraphQL — Code-First

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.

4 min read Level 3/5 #nestjs#graphql
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 →