NestJS en GraphQL in 2026: Schema's, Resolvers en Sollicitatievragen
De integratie van NestJS met GraphQL maakt de ontwikkeling van type-safe API's mogelijk. Deze tutorial behandelt code-first en schema-first benaderingen, resolver-patronen en sollicitatievragen voor 2026.

De integratie van NestJS met GraphQL biedt een gestructureerde aanpak voor het bouwen van type-safe API's die TypeScript-decorators combineren met de querytaal van GraphQL. Deze tutorial behandelt zowel de schema-first als code-first benadering, resolver-patronen en de sollicitatievragen die hiring managers in 2026 stellen.
NestJS 11 gebruikt standaard code-first GraphQL en genereert het schema uit TypeScript-klassen. Schema-first blijft beschikbaar voor teams die werken met bestaande .graphql-bestanden of SDL-gebaseerde workflows.
NestJS GraphQL Configureren met Apollo Server
NestJS integreert met Apollo Server via het @nestjs/graphql-pakket. De configuratie verschilt afhankelijk van de gekozen benadering—code-first genereert SDL uit decorators, terwijl schema-first .graphql-bestanden direct parseert.
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'), // Code-first: generates schema
sortSchema: true, // Alphabetical ordering for readability
playground: process.env.NODE_ENV !== 'production', // Disable in prod
introspection: process.env.NODE_ENV !== 'production',
}),
],
})
export class AppModule {}De optie autoSchemaFile schakelt de code-first modus in. Wanneer deze op true wordt gezet, wordt het schema in het geheugen gegenereerd zonder naar schijf te schrijven—handig voor serverless deployments waar bestandssysteemtoegang beperkt kan zijn.
GraphQL Types Definiëren met Code-First Decorators
Code-first definieert GraphQL types met behulp van TypeScript-klassen gedecoreerd met @ObjectType(). Elk veld gebruikt @Field() om het GraphQL-type en nullability te specificeren.
import { ObjectType, Field, ID, Int } from '@nestjs/graphql';
@ObjectType({ description: 'Application user' }) // Description appears in schema docs
export class User {
@Field(() => ID) // Maps to GraphQL ID scalar
id: string;
@Field()
email: string;
@Field({ nullable: true }) // Optional field in GraphQL
displayName?: string;
@Field(() => Int, { defaultValue: 0 })
postCount: number;
@Field(() => [Post], { nullable: 'itemsAndList' }) // Both list and items can be null
posts?: Post[];
// Fields without @Field() are excluded from GraphQL schema
passwordHash: string;
}De optie nullable accepteert drie waarden: true (veld is optioneel), 'items' (lijst-items kunnen null zijn) en 'itemsAndList' (zowel de lijst als items kunnen null zijn). Deze granulariteit komt exact overeen met de nullability-semantiek van GraphQL.
Resolvers Bouwen voor Queries en Mutations
Resolvers verwerken inkomende GraphQL-operaties. NestJS gebruikt @Resolver() om een klasse als resolver te markeren, waarbij method-decorators de operatietypes specificeren.
import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql';
import { User } from './user.entity';
import { UsersService } from './users.service';
import { CreateUserInput } from './dto/create-user.input';
@Resolver(() => User) // Binds resolver to User type for field resolution
export class UsersResolver {
constructor(private readonly usersService: UsersService) {}
@Query(() => [User], { name: 'users' }) // Explicit query name
findAll(): Promise<User[]> {
return this.usersService.findAll();
}
@Query(() => User, { nullable: true })
user(@Args('id', { type: () => ID }) id: string): Promise<User | null> {
return this.usersService.findOne(id);
}
@Mutation(() => User)
createUser(@Args('input') input: CreateUserInput): Promise<User> {
return this.usersService.create(input);
}
}De decorator @Resolver(() => User) stelt de context in voor @ResolveField()-decorators, waardoor veld-niveau resolutie voor berekende of gerelateerde data mogelijk wordt.
Input Types en Validatie met class-validator
GraphQL input types definiëren mutation payloads. Het combineren van @InputType() met class-validator decorators maakt zowel schema-niveau als runtime validatie mogelijk.
import { InputType, Field } from '@nestjs/graphql';
import { IsEmail, MinLength, IsOptional, Matches } from 'class-validator';
@InputType()
export class CreateUserInput {
@Field()
@IsEmail({}, { message: 'Invalid email format' })
email: string;
@Field()
@MinLength(8, { message: 'Password must be at least 8 characters' })
@Matches(/[A-Z]/, { message: 'Password must contain uppercase letter' })
password: string;
@Field({ nullable: true })
@IsOptional()
@MinLength(2)
displayName?: string;
}Validatie wordt globaal ingeschakeld door de ValidationPipe toe te voegen in main.ts. GraphQL-fouten bevatten validatieberichten in het extensions-veld, wat client-compatibiliteit waarborgt.
Klaar om je Node.js / NestJS gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Gerelateerde Data Resolven met @ResolveField en DataLoader
Field resolvers behandelen relaties tussen types. Zonder optimalisatie triggert het ophalen van een lijst gebruikers met hun posts N+1 queries—één voor gebruikers, dan één per gebruiker voor posts.
import { Resolver, ResolveField, Parent } from '@nestjs/graphql';
import { User } from './user.entity';
import { Post } from '../posts/post.entity';
import { PostsLoader } from '../posts/posts.loader';
@Resolver(() => User)
export class UsersResolver {
constructor(private readonly postsLoader: PostsLoader) {}
@ResolveField(() => [Post])
async posts(@Parent() user: User): Promise<Post[]> {
// DataLoader batches requests: one query for all user IDs
return this.postsLoader.batchByUserId.load(user.id);
}
@ResolveField(() => Int)
async postCount(@Parent() user: User): Promise<number> {
const posts = await this.postsLoader.batchByUserId.load(user.id);
return posts.length;
}
}DataLoader bundelt en cacht verzoeken binnen een enkele GraphQL-operatie. Voor NestJS wordt de DataLoader op request-niveau voorzien met behulp van @Injectable({ scope: Scope.REQUEST }).
import { Injectable, Scope } from '@nestjs/common';
import * as DataLoader from 'dataloader';
import { PostsService } from './posts.service';
import { Post } from './post.entity';
@Injectable({ scope: Scope.REQUEST }) // New instance per request
export class PostsLoader {
constructor(private readonly postsService: PostsService) {}
public readonly batchByUserId = new DataLoader<string, Post[]>(
async (userIds: readonly string[]) => {
// Single query: SELECT * FROM posts WHERE user_id IN (...)
const posts = await this.postsService.findByUserIds([...userIds]);
// Map results back to input order
const postsMap = new Map<string, Post[]>();
posts.forEach(post => {
const existing = postsMap.get(post.userId) || [];
postsMap.set(post.userId, [...existing, post]);
});
return userIds.map(id => postsMap.get(id) || []);
}
);
}DataLoader reduceert N+1 queries tot een enkele gebundelde query, cruciaal voor GraphQL API's waar clients de query-diepte bepalen. Voor diepere behandeling van NestJS-architectuurpatronen, zie de NestJS Module & Dependency Injection sollicitatievragen.
Subscriptions voor Real-Time Data
GraphQL subscriptions sturen data naar clients via WebSocket-verbindingen. NestJS gebruikt de graphql-ws-bibliotheek, die het GraphQL over WebSocket protocol implementeert.
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
subscriptions: {
'graphql-ws': true, // Modern protocol
'subscriptions-transport-ws': false, // Deprecated legacy protocol
},
}),import { Resolver, Subscription } from '@nestjs/graphql';
import { PubSub } from 'graphql-subscriptions';
import { Post } from './post.entity';
const pubSub = new PubSub(); // Use Redis PubSub for multi-instance deployments
@Resolver(() => Post)
export class PostsResolver {
@Subscription(() => Post, {
filter: (payload, variables) =>
payload.postCreated.userId === variables.userId, // Client-side filtering
})
postCreated() {
return pubSub.asyncIterableIterator('postCreated');
}
@Mutation(() => Post)
async createPost(@Args('input') input: CreatePostInput): Promise<Post> {
const post = await this.postsService.create(input);
pubSub.publish('postCreated', { postCreated: post }); // Trigger subscription
return post;
}
}Voor productie-deployments over meerdere instanties wordt de in-memory PubSub vervangen door graphql-redis-subscriptions om events cluster-breed te distribueren.
Authenticatie en Autorisatie in GraphQL
NestJS Guards werken naadloos met GraphQL resolvers. De executie-context verschilt van REST—gebruik GqlExecutionContext om het request te extraheren.
import { Injectable, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req; // Extract request from GraphQL context
}
}Guards worden toegepast op resolver- of method-niveau. Autorisatie op veldniveau gebruikt @ResolveField() met conditionele logica gebaseerd op gebruikersrollen.
@UseGuards(GqlAuthGuard)
@Resolver(() => User)
export class UsersResolver {
@Query(() => User)
me(@CurrentUser() user: User): User {
return user; // Return authenticated user
}
@ResolveField(() => String, { nullable: true })
email(@Parent() user: User, @CurrentUser() currentUser: User): string | null {
// Only return email if viewing own profile or admin
if (user.id === currentUser.id || currentUser.role === 'ADMIN') {
return user.email;
}
return null;
}
}Voor complexe autorisatie kunt u GraphQL Shield of de ingebouwde CASL-integratie van NestJS overwegen. Het artikel NestJS Guards en Interceptors behandelt deze patronen in detail.
Veelgestelde NestJS GraphQL Sollicitatievragen
Technische sollicitatiegesprekken voor NestJS-posities bevatten vaak GraphQL-specifieke vragen. Hier zijn de patronen die hiring managers evalueren.
V: Hoe behandelt NestJS het N+1 probleem in GraphQL?
DataLoader bundelt field resolver calls binnen een enkel request. Wanneer meerdere parent-objecten hetzelfde veld opvragen, verzamelt DataLoader alle keys, voert één gebundelde query uit en distribueert de resultaten. De loader moet request-scoped zijn om caching-problemen tussen requests te voorkomen.
V: Wat is het verschil tussen code-first en schema-first in NestJS GraphQL?
Code-first genereert het GraphQL-schema uit TypeScript-decorators tijdens runtime, waardoor types en schema automatisch gesynchroniseerd blijven. Schema-first parseert .graphql SDL-bestanden, wat handmatige typedefinities vereist. Code-first is geschikt voor TypeScript-native teams; schema-first werkt beter wanneer het schema het contract is tussen frontend- en backend-teams.
V: Hoe zou men veld-niveau permissies implementeren?
Er bestaan drie benaderingen: (1) @ResolveField() met conditionele returns gebaseerd op gebruikerscontext, (2) Aangepaste decorators die permissies controleren vóór veldresolutie, (3) Schema-directieven zoals @auth(requires: ADMIN) verwerkt door een directive transformer. De eerste benadering biedt de meeste flexibiliteit; directieven bieden de schoonste schema-documentatie.
V: Leg de GraphQL-context uit in NestJS.
Het context-object passeert door alle resolvers binnen een request. NestJS vult het standaard met het HTTP-request. Aangepaste context wordt geconfigureerd in GraphQLModule.forRoot() via de context-optie—nuttig voor het toevoegen van DataLoader-instanties, geauthenticeerde gebruiker of databaseverbindingen.
V: Hoe schalen subscriptions over meerdere server-instanties?
In-memory PubSub werkt alleen voor single-instance deployments. Multi-instance architecturen vereisen een externe broker—Redis PubSub is standaard. Elke server-instantie abonneert op Redis-kanalen; wanneer een instantie een event publiceert, ontvangen alle instanties het en sturen het naar verbonden WebSocket-clients.
Voor aanvullende NestJS-sollicitatievoorbereiding kan de module Middleware en Interceptors verkend worden, die request lifecycle-patronen behandelt.
Conclusie
- NestJS GraphQL ondersteunt zowel code-first als schema-first benaderingen—code-first vereenvoudigt TypeScript-projecten, schema-first is geschikt voor SDL-gestuurde workflows
- DataLoader elimineert N+1 queries door field resolver requests te bundelen binnen een enkele operatie
- Request-scoped DataLoader-instanties voorkomen cache-vervuiling bij gelijktijdige requests
GqlExecutionContextverbindt NestJS Guards met de resolver-context van GraphQL- Productie-subscriptions vereisen Redis PubSub voor multi-instance event distributie
- Autorisatie op veldniveau combineert
@ResolveField()met gebruikerscontext-controles
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Delen
Gerelateerde artikelen

NestJS en TypeORM in 2026: Migraties, Relaties en Interviewvragen
NestJS en TypeORM vormen een beproefd duo voor schaalbare backend-applicaties. Dit artikel behandelt configuratie, migraties, relaties, transacties en interviewvragen.

Node.js 24 in 2026: URLPattern, Permission Model en Sollicitatievragen
Node.js 24 LTS brengt een stabiel Permission Model, globale URLPattern API, expliciet resourcebeheer met using/await using en V8 13.6. Een diepgaande analyse van de functies die relevant zijn voor productie en technische sollicitatiegesprekken.

Microservices met NestJS in 2026: Architectuur, gRPC en Sollicitatievragen
Een uitgebreide gids over het bouwen van schaalbare microservices met NestJS, inclusief gRPC-integratie, betrouwbaarheidspatronen en veelgestelde sollicitatievragen voor senior ontwikkelaars.