# 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. - Published: 2026-07-25 - Updated: 2026-07-25 - Author: SharpSkill - Reading time: 5 min --- 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. > **Code-First vs Schema-First** > > 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](https://www.apollographql.com/docs/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. ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { join } from 'path'; @Module({ imports: [ GraphQLModule.forRoot({ 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. ```typescript // user.entity.ts 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](https://graphql.org/learn/schema/#lists-and-non-null). ## 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. ```typescript // users.resolver.ts 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 { return this.usersService.findAll(); } @Query(() => User, { nullable: true }) user(@Args('id', { type: () => ID }) id: string): Promise { return this.usersService.findOne(id); } @Mutation(() => User) createUser(@Args('input') input: CreateUserInput): Promise { 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](https://github.com/typestack/class-validator)-Dekoratoren ermöglicht sowohl Schema-Level als auch Laufzeit-Validierung. ```typescript // dto/create-user.input.ts 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. ## 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. ```typescript // users.resolver.ts 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 { // DataLoader batches requests: one query for all user IDs return this.postsLoader.batchByUserId.load(user.id); } @ResolveField(() => Int) async postCount(@Parent() user: User): Promise { 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. ```typescript // posts/posts.loader.ts 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( 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(); 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](/technologies/node-nestjs/interview-questions/nestjs-modules-di). ## 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](https://github.com/enisdenjo/graphql-ws) implementiert. ```typescript // app.module.ts - Enable subscriptions GraphQLModule.forRoot({ driver: ApolloDriver, autoSchemaFile: true, subscriptions: { 'graphql-ws': true, // Modern protocol 'subscriptions-transport-ws': false, // Deprecated legacy protocol }, }), ``` ```typescript // posts.resolver.ts 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 { 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. ```typescript // guards/gql-auth.guard.ts 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. ```typescript // users.resolver.ts @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](https://github.com/dimatill/graphql-shield) oder die integrierte CASL-Integration von NestJS an. Der Artikel [NestJS Guards und Interceptors](/blog/node-nestjs/nestjs-guards-interceptors-modular-architecture) 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](/technologies/node-nestjs/interview-questions/middleware-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 - `GqlExecutionContext` verbindet 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 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/node-nestjs/nestjs-graphql-schemas-resolvers-tutorial