Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
ramziddin avatar

Graphql

  • 1 installs
  • 1 repo stars
  • Updated January 2, 2026
  • ramziddin/ccplugins

Reference for NestJS GraphQL: Code First and Schema First approaches, resolvers, queries, mutations, subscriptions, and Apollo or Mercurius drivers.

About

Explains building GraphQL APIs in NestJS with code-first or schema-first setups, resolvers, and real-time subscriptions. A developer uses it when creating a GraphQL API instead of REST in NestJS.

  • Supports Code First (decorators) and Schema First (SDL) approaches
  • Works with Apollo Server and Mercurius drivers

Graphql by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #3,836 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/ramziddin/ccplugins --skill graphql

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars1
Last updatedJanuary 2, 2026
Repositoryramziddin/ccplugins

What it does

Reference for NestJS GraphQL: Code First and Schema First approaches, resolvers, queries, mutations, subscriptions, and Apollo or Mercurius drivers.

Files

SKILL.mdMarkdownGitHub ↗

NestJS GraphQL

When to Use This Skill

Use this skill when:

  • Building GraphQL APIs instead of REST
  • Creating resolvers for queries and mutations
  • Implementing real-time features with subscriptions
  • Working with GraphQL schemas (SDL or code-first)
  • Setting up Apollo Server or Mercurius with NestJS
  • Creating type-safe GraphQL APIs with TypeScript

What is GraphQL in NestJS?

NestJS provides first-class support for GraphQL with two approaches: Code First (using decorators and TypeScript classes) and Schema First (using GraphQL SDL files). It supports Apollo Server and Mercurius drivers.

Installation

npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql

For Mercurius (Fastify):

npm i @nestjs/graphql @nestjs/mercurius @apollo/server graphql mercurius

Code First Approach

Setup

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { join } from 'path';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
    }),
  ],
})
export class AppModule {}

Key Points:

  • autoSchemaFile - Automatically generates schema from TypeScript classes
  • driver: ApolloDriver - Uses Apollo Server
  • Schema is generated at runtime from decorators

Object Types

import { ObjectType, Field, Int, ID } from '@nestjs/graphql';

@ObjectType()
export class Cat {
  @Field(() => ID)
  id: string;

  @Field()
  name: string;

  @Field(() => Int)
  age: number;

  @Field({ nullable: true })
  breed?: string;
}

Resolvers and Queries

import { Resolver, Query, Args, Int } from '@nestjs/graphql';
import { Cat } from './models/cat.model';
import { CatsService } from './cats.service';

@Resolver(() => Cat)
export class CatsResolver {
  constructor(private catsService: CatsService) {}

  @Query(() => [Cat])
  async cats(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  @Query(() => Cat)
  async cat(@Args('id', { type: () => ID }) id: string): Promise<Cat> {
    return this.catsService.findOne(id);
  }

  @Query(() => [Cat])
  async searchCats(
    @Args('name', { nullable: true }) name?: string,
    @Args('age', { type: () => Int, nullable: true }) age?: number,
  ): Promise<Cat[]> {
    return this.catsService.search(name, age);
  }
}

Key Points:

  • @Resolver() - Marks class as GraphQL resolver
  • @Query() - Defines GraphQL query
  • Return type specified with arrow function
  • @Args() - Extracts query arguments

Mutations

import { Resolver, Mutation, Args } from '@nestjs/graphql';
import { Cat } from './models/cat.model';
import { CreateCatInput } from './dto/create-cat.input';
import { UpdateCatInput } from './dto/update-cat.input';

@Resolver(() => Cat)
export class CatsResolver {
  constructor(private catsService: CatsService) {}

  @Mutation(() => Cat)
  async createCat(
    @Args('createCatInput') createCatInput: CreateCatInput,
  ): Promise<Cat> {
    return this.catsService.create(createCatInput);
  }

  @Mutation(() => Cat)
  async updateCat(
    @Args('id') id: string,
    @Args('updateCatInput') updateCatInput: UpdateCatInput,
  ): Promise<Cat> {
    return this.catsService.update(id, updateCatInput);
  }

  @Mutation(() => Boolean)
  async deleteCat(@Args('id') id: string): Promise<boolean> {
    await this.catsService.remove(id);
    return true;
  }
}

Input Types

import { InputType, Field, Int } from '@nestjs/graphql';

@InputType()
export class CreateCatInput {
  @Field()
  name: string;

  @Field(() => Int)
  age: number;

  @Field({ nullable: true })
  breed?: string;
}

@InputType()
export class UpdateCatInput {
  @Field({ nullable: true })
  name?: string;

  @Field(() => Int, { nullable: true })
  age?: number;

  @Field({ nullable: true })
  breed?: string;
}

Subscriptions

import { Resolver, Subscription } from '@nestjs/graphql';
import { PubSub } from 'graphql-subscriptions';
import { Cat } from './models/cat.model';

const pubSub = new PubSub();

@Resolver(() => Cat)
export class CatsResolver {
  @Mutation(() => Cat)
  async createCat(
    @Args('createCatInput') createCatInput: CreateCatInput,
  ): Promise<Cat> {
    const cat = await this.catsService.create(createCatInput);
    pubSub.publish('catAdded', { catAdded: cat });
    return cat;
  }

  @Subscription(() => Cat)
  catAdded() {
    return pubSub.asyncIterator('catAdded');
  }
}

Enable subscriptions in module:

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  autoSchemaFile: true,
  subscriptions: {
    'graphql-ws': true,
  },
})

Schema First Approach

Setup

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      typePaths: ['./**/*.graphql'],
      definitions: {
        path: join(process.cwd(), 'src/graphql.ts'),
      },
    }),
  ],
})
export class AppModule {}

Schema Definition (.graphql)

# cats.graphql
type Cat {
  id: ID!
  name: String!
  age: Int!
  breed: String
}

input CreateCatInput {
  name: String!
  age: Int!
  breed: String
}

type Query {
  cats: [Cat!]!
  cat(id: ID!): Cat
}

type Mutation {
  createCat(createCatInput: CreateCatInput!): Cat!
  deleteCat(id: ID!): Boolean!
}

type Subscription {
  catAdded: Cat!
}

Resolver Implementation

import { Resolver, Query, Mutation, Args, Subscription } from '@nestjs/graphql';
import { CatsService } from './cats.service';
import { PubSub } from 'graphql-subscriptions';

const pubSub = new PubSub();

@Resolver('Cat')
export class CatsResolver {
  constructor(private catsService: CatsService) {}

  @Query('cats')
  async getCats() {
    return this.catsService.findAll();
  }

  @Query('cat')
  async getCat(@Args('id') id: string) {
    return this.catsService.findOne(id);
  }

  @Mutation('createCat')
  async create(@Args('createCatInput') args: any) {
    const cat = await this.catsService.create(args);
    pubSub.publish('catAdded', { catAdded: cat });
    return cat;
  }

  @Subscription('catAdded')
  catAdded() {
    return pubSub.asyncIterator('catAdded');
  }
}

GraphQL Modules

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: true,
    }),
    CatsModule,
  ],
})
export class AppModule {}
import { Module } from '@nestjs/common';
import { CatsResolver } from './cats.resolver';
import { CatsService } from './cats.service';

@Module({
  providers: [CatsResolver, CatsService],
})
export class CatsModule {}

Async Configuration

GraphQLModule.forRootAsync<ApolloDriverConfig>({
  driver: ApolloDriver,
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    autoSchemaFile: true,
    playground: configService.get('GRAPHQL_PLAYGROUND') === 'true',
  }),
  inject: [ConfigService],
})

Field Resolvers

import { Resolver, Query, ResolveField, Parent } from '@nestjs/graphql';
import { Cat } from './models/cat.model';
import { Owner } from './models/owner.model';

@Resolver(() => Cat)
export class CatsResolver {
  @Query(() => Cat)
  async cat(@Args('id') id: string): Promise<Cat> {
    return this.catsService.findOne(id);
  }

  @ResolveField(() => Owner)
  async owner(@Parent() cat: Cat): Promise<Owner> {
    return this.ownersService.findByOwnerId(cat.ownerId);
  }
}

Guards and Interceptors

import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { AuthGuard } from '../auth/auth.guard';
import { Cat } from './models/cat.model';

@Resolver(() => Cat)
export class CatsResolver {
  @Query(() => [Cat])
  @UseGuards(AuthGuard)
  async cats(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  @Mutation(() => Cat)
  @UseGuards(AuthGuard)
  async createCat(
    @Args('createCatInput') createCatInput: CreateCatInput,
  ): Promise<Cat> {
    return this.catsService.create(createCatInput);
  }
}

Custom Scalars

import { Scalar, CustomScalar } from '@nestjs/graphql';
import { Kind, ValueNode } from 'graphql';

@Scalar('Date')
export class DateScalar implements CustomScalar<number, Date> {
  description = 'Date custom scalar type';

  parseValue(value: number): Date {
    return new Date(value);
  }

  serialize(value: Date): number {
    return value.getTime();
  }

  parseLiteral(ast: ValueNode): Date {
    if (ast.kind === Kind.INT) {
      return new Date(ast.value);
    }
    return null;
  }
}

Register in module:

@Module({
  providers: [DateScalar, CatsResolver],
})
export class CatsModule {}

GraphQL Playground

Access at http://localhost:3000/graphql (enabled by default in development)

Disable in production:

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  autoSchemaFile: true,
  playground: false,
})

Best Practices

1. Use Code First for TypeScript projects - Better type safety and DRY principle 2. Use Schema First when - Schema is designed by separate team or using schema-stitching 3. Separate models and inputs - Don't use same class for both 4. Use field resolvers - For computed fields and lazy loading 5. Implement proper error handling - Use GraphQL error formatting 6. Use DataLoader - Prevent N+1 query problems 7. Enable depth limiting - Prevent deeply nested queries 8. Use subscriptions wisely - Consider scaling implications 9. Document schema - Add descriptions to types and fields 10. Use validation - Validate inputs with class-validator

Common Patterns

Pagination

import { ObjectType, Field, Int } from '@nestjs/graphql';

@ObjectType()
export class PaginatedCats {
  @Field(() => [Cat])
  items: Cat[];

  @Field(() => Int)
  total: number;

  @Field(() => Int)
  page: number;

  @Field(() => Int)
  pageSize: number;
}

@Resolver(() => Cat)
export class CatsResolver {
  @Query(() => PaginatedCats)
  async paginatedCats(
    @Args('page', { type: () => Int, defaultValue: 1 }) page: number,
    @Args('pageSize', { type: () => Int, defaultValue: 10 }) pageSize: number,
  ): Promise<PaginatedCats> {
    const [items, total] = await this.catsService.findPaginated(page, pageSize);
    return { items, total, page, pageSize };
  }
}

Enums

import { registerEnumType } from '@nestjs/graphql';

export enum CatBreed {
  PERSIAN = 'PERSIAN',
  SIAMESE = 'SIAMESE',
  MAINE_COON = 'MAINE_COON',
}

registerEnumType(CatBreed, {
  name: 'CatBreed',
  description: 'Available cat breeds',
});

@ObjectType()
export class Cat {
  @Field(() => CatBreed, { nullable: true })
  breed?: CatBreed;
}

Unions and Interfaces

import { createUnionType, Field, InterfaceType, ObjectType } from '@nestjs/graphql';

@InterfaceType()
abstract class Animal {
  @Field()
  name: string;

  @Field()
  age: number;
}

@ObjectType({ implements: () => [Animal] })
export class Cat implements Animal {
  name: string;
  age: number;

  @Field()
  breed: string;
}

@ObjectType({ implements: () => [Animal] })
export class Dog implements Animal {
  name: string;
  age: number;

  @Field()
  isGoodBoy: boolean;
}

export const AnimalUnion = createUnionType({
  name: 'AnimalUnion',
  types: () => [Cat, Dog] as const,
});

Code First vs Schema First

Code First Advantages:

  • Single source of truth (TypeScript)
  • Better IDE support and type safety
  • Less duplication
  • Easier refactoring

Schema First Advantages:

  • Schema-driven development
  • Better for contract-first APIs
  • Non-TypeScript teams can design schema
  • Schema stitching and federation easier

Choose based on team preferences and project requirements.

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.