NestJS und GraphQL 2026: Schemas, Resolver und Interviewfragen
Die Integration von NestJS mit GraphQL ermöglicht die Entwicklung typsicherer APIs. Dieses Tutorial behandelt Code-First und Schema-First Ansätze, Resolver-Muster und Interviewfragen für 2026.

Die Integration von NestJS mit GraphQL ermöglicht die Entwicklung typsicherer APIs, die TypeScript-Dekoratoren mit der Abfragesprache von GraphQL kombinieren. Dieses Tutorial behandelt sowohl den Schema-First als auch den Code-First Ansatz, Resolver-Muster und die Interviewfragen, die Personalverantwortliche im Jahr 2026 stellen.
NestJS 11 verwendet standardmäßig Code-First GraphQL und generiert das Schema aus TypeScript-Klassen. Schema-First bleibt für Teams verfügbar, die bereits mit .graphql-Dateien oder SDL-basierten Workflows arbeiten.
NestJS GraphQL mit Apollo Server einrichten
NestJS integriert sich über das @nestjs/graphql-Paket mit Apollo Server. Die Konfiguration unterscheidet sich je nach gewähltem Ansatz—Code-First generiert SDL aus Dekoratoren, während Schema-First .graphql-Dateien direkt parst.
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 {}Die Option autoSchemaFile aktiviert den Code-First Modus. Wird sie auf true gesetzt, wird das Schema im Speicher generiert, ohne auf die Festplatte geschrieben zu werden—nützlich für Serverless-Deployments, bei denen der Dateisystemzugriff eingeschränkt sein kann.
GraphQL-Typen mit Code-First Dekoratoren definieren
Code-First definiert GraphQL-Typen mithilfe von TypeScript-Klassen, die mit @ObjectType() dekoriert sind. Jedes Feld verwendet @Field(), um seinen GraphQL-Typ und die Nullfähigkeit anzugeben.
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;
}Die Option nullable akzeptiert drei Werte: true (Feld ist optional), 'items' (Listenelemente können null sein) und 'itemsAndList' (sowohl die Liste als auch die Elemente können null sein). Diese Granularität entspricht exakt der Nullfähigkeitssemantik von GraphQL.
Resolver für Queries und Mutations erstellen
Resolver verarbeiten eingehende GraphQL-Operationen. NestJS verwendet @Resolver(), um eine Klasse als Resolver zu markieren, wobei Methoden-Dekoratoren die Operationstypen spezifizieren.
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);
}
}Der Dekorator @Resolver(() => User) etabliert den Kontext für @ResolveField()-Dekoratoren und ermöglicht die Auflösung auf Feldebene für berechnete oder verknüpfte Daten.
Eingabetypen und Validierung mit class-validator
GraphQL-Eingabetypen definieren Mutations-Payloads. Die Kombination von @InputType() mit class-validator-Dekoratoren ermöglicht sowohl Schema-Level als auch Laufzeit-Validierung.
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;
}Die Validierung wird global aktiviert, indem die ValidationPipe in main.ts hinzugefügt wird. GraphQL-Fehler enthalten Validierungsmeldungen im extensions-Feld, was die Client-Kompatibilität gewährleistet.
Bereit für deine Node.js / NestJS-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Verknüpfte Daten mit @ResolveField und DataLoader auflösen
Feld-Resolver verarbeiten Beziehungen zwischen Typen. Ohne Optimierung löst das Abrufen einer Liste von Benutzern mit ihren Beiträgen N+1 Abfragen aus—eine für Benutzer, dann eine pro Benutzer für Beiträge.
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 bündelt und cached Anfragen innerhalb einer einzelnen GraphQL-Operation. Für NestJS wird der DataLoader auf Request-Ebene mittels @Injectable({ scope: Scope.REQUEST }) bereitgestellt.
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 reduziert N+1 Abfragen auf eine einzige gebündelte Abfrage, was für GraphQL-APIs entscheidend ist, bei denen Clients die Abfragetiefe kontrollieren. Für eine tiefere Behandlung von NestJS-Architekturmustern siehe die NestJS Module & Dependency Injection Interviewfragen.
Subscriptions für Echtzeitdaten
GraphQL-Subscriptions übertragen Daten über WebSocket-Verbindungen an Clients. NestJS verwendet die graphql-ws-Bibliothek, die das GraphQL over WebSocket Protokoll implementiert.
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;
}
}Für Produktions-Deployments über mehrere Instanzen hinweg wird die In-Memory PubSub durch graphql-redis-subscriptions ersetzt, um Ereignisse clusterweit zu verteilen.
Authentifizierung und Autorisierung in GraphQL
NestJS Guards funktionieren nahtlos mit GraphQL-Resolvern. Der Ausführungskontext unterscheidet sich von REST—GqlExecutionContext wird verwendet, um die Anfrage zu extrahieren.
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 werden auf Resolver- oder Methodenebene angewendet. Autorisierung auf Feldebene verwendet @ResolveField() mit bedingter Logik basierend auf Benutzerrollen.
@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;
}
}Für komplexe Autorisierung bietet sich GraphQL Shield oder die integrierte CASL-Integration von NestJS an. Der Artikel NestJS Guards und Interceptors behandelt diese Muster ausführlich.
Häufige NestJS GraphQL Interviewfragen
Technische Interviews für NestJS-Positionen beinhalten häufig GraphQL-spezifische Fragen. Hier sind die Muster, die Personalverantwortliche bewerten.
F: Wie behandelt NestJS das N+1-Problem in GraphQL?
DataLoader bündelt Feld-Resolver-Aufrufe innerhalb einer einzelnen Anfrage. Wenn mehrere übergeordnete Objekte dasselbe Feld anfordern, sammelt DataLoader alle Schlüssel, führt eine gebündelte Abfrage aus und verteilt die Ergebnisse. Der Loader muss request-scoped sein, um Caching-Probleme zwischen Anfragen zu vermeiden.
F: Was ist der Unterschied zwischen Code-First und Schema-First in NestJS GraphQL?
Code-First generiert das GraphQL-Schema zur Laufzeit aus TypeScript-Dekoratoren und hält Typen und Schema automatisch synchron. Schema-First parst .graphql SDL-Dateien, was manuelle Typdefinitionen erfordert. Code-First eignet sich für TypeScript-native Teams; Schema-First funktioniert besser, wenn das Schema der Vertrag zwischen Frontend- und Backend-Teams ist.
F: Wie würde man Berechtigungen auf Feldebene implementieren?
Es existieren drei Ansätze: (1) @ResolveField() mit bedingten Rückgaben basierend auf dem Benutzerkontext, (2) Benutzerdefinierte Dekoratoren, die Berechtigungen vor der Feldauflösung prüfen, (3) Schema-Direktiven wie @auth(requires: ADMIN), die von einem Direktiven-Transformer verarbeitet werden. Der erste Ansatz bietet die größte Flexibilität; Direktiven bieten die sauberste Schema-Dokumentation.
F: Erklären Sie den GraphQL-Kontext in NestJS.
Das Kontextobjekt wird durch alle Resolver innerhalb einer Anfrage weitergereicht. NestJS befüllt es standardmäßig mit der HTTP-Anfrage. Benutzerdefinierter Kontext wird in GraphQLModule.forRoot() über die context-Option konfiguriert—nützlich zum Hinzufügen von DataLoader-Instanzen, authentifizierten Benutzern oder Datenbankverbindungen.
F: Wie skalieren Subscriptions über mehrere Server-Instanzen?
In-Memory PubSub funktioniert nur für Single-Instance-Deployments. Multi-Instance-Architekturen erfordern einen externen Broker—Redis PubSub ist Standard. Jede Server-Instanz abonniert Redis-Kanäle; wenn eine Instanz ein Ereignis veröffentlicht, erhalten alle Instanzen es und senden es an verbundene WebSocket-Clients.
Für zusätzliche NestJS-Interviewvorbereitung sollte das Modul Middleware und Interceptors erkundet werden, das Request-Lifecycle-Muster behandelt.
Fazit
- NestJS GraphQL unterstützt sowohl Code-First als auch Schema-First Ansätze—Code-First vereinfacht TypeScript-Projekte, Schema-First eignet sich für SDL-gesteuerte Workflows
- DataLoader eliminiert N+1 Abfragen durch Bündelung von Feld-Resolver-Anfragen innerhalb einer einzelnen Operation
- Request-scoped DataLoader-Instanzen verhindern Cache-Verschmutzung bei gleichzeitigen Anfragen
GqlExecutionContextverbindet NestJS Guards mit dem Resolver-Kontext von GraphQL- Produktions-Subscriptions erfordern Redis PubSub für Multi-Instance Event-Verteilung
- Autorisierung auf Feldebene kombiniert
@ResolveField()mit Benutzerkontext-Prüfungen
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Teilen
Verwandte Artikel

NestJS und TypeORM 2026: Migrationen, Relationen und Interviewfragen
NestJS und TypeORM gehören 2026 zu den etabliertesten Kombinationen im Node.js-Backend-Bereich. Dieser Artikel behandelt Migrationen, Relationen, Transaktionen und Interviewfragen.

Node.js 24 im Jahr 2026: URLPattern, Permission Model und Interviewfragen
Node.js 24 LTS bringt ein stabiles Permission Model, globale URLPattern-API, explizites Ressourcenmanagement mit using/await using und V8 13.6. Ein detaillierter Einblick in die Funktionen, die für Produktion und Vorstellungsgespräche relevant sind.

Microservices mit NestJS in 2026: Architektur, gRPC und Interview-Fragen
Ein umfassender Leitfaden zur Entwicklung von Microservices mit NestJS, einschließlich gRPC-Integration, Kommunikationsmustern und typischen Fragen für technische Vorstellungsgespräche.