
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 graphqlAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 1 |
| Last updated | January 2, 2026 |
| Repository | ramziddin/ccplugins ↗ |
What it does
Reference for NestJS GraphQL: Code First and Schema First approaches, resolvers, queries, mutations, subscriptions, and Apollo or Mercurius drivers.
Files
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 graphqlFor Mercurius (Fastify):
npm i @nestjs/graphql @nestjs/mercurius @apollo/server graphql mercuriusCode 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 classesdriver: 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.