# NestJS e GraphQL nel 2026: Schema, Resolver e Domande per Colloqui > L'integrazione di NestJS con GraphQL consente lo sviluppo di API type-safe. Questo tutorial copre gli approcci code-first e schema-first, pattern di resolver e domande da colloquio per il 2026. - Published: 2026-07-25 - Updated: 2026-07-25 - Author: SharpSkill - Reading time: 5 min --- L'integrazione di NestJS con GraphQL offre un approccio strutturato per costruire API type-safe che sfruttano i decoratori TypeScript e il linguaggio di query di GraphQL. Questo tutorial copre sia l'approccio schema-first che code-first, i pattern dei resolver e le domande che i responsabili delle assunzioni pongono nel 2026. > **Code-First vs Schema-First** > > NestJS 11 utilizza di default GraphQL code-first, generando lo schema dalle classi TypeScript. Schema-first rimane disponibile per i team che lavorano con file `.graphql` esistenti o workflow basati su SDL. ## Configurazione di NestJS GraphQL con Apollo Server NestJS si integra con [Apollo Server](https://www.apollographql.com/docs/apollo-server/) attraverso il pacchetto `@nestjs/graphql`. La configurazione differisce in base all'approccio scelto—code-first genera SDL dai decoratori, mentre schema-first analizza direttamente i file `.graphql`. ```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 {} ``` L'opzione `autoSchemaFile` abilita la modalità code-first. Impostandola su `true`, lo schema viene generato in memoria senza scrittura su disco—utile per deployment serverless dove l'accesso al filesystem potrebbe essere limitato. ## Definire Tipi GraphQL con Decoratori Code-First Code-first definisce i tipi GraphQL utilizzando classi TypeScript decorate con `@ObjectType()`. Ogni campo utilizza `@Field()` per specificare il suo tipo GraphQL e la nullabilità. ```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; } ``` L'opzione `nullable` accetta tre valori: `true` (campo opzionale), `'items'` (gli elementi della lista possono essere null) e `'itemsAndList'` (sia la lista che gli elementi possono essere null). Questa granularità corrisponde esattamente alla [semantica di nullabilità di GraphQL](https://graphql.org/learn/schema/#lists-and-non-null). ## Costruire Resolver per Query e Mutation I resolver gestiscono le operazioni GraphQL in arrivo. NestJS utilizza `@Resolver()` per contrassegnare una classe come resolver, con decoratori di metodo che specificano i tipi di operazione. ```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); } } ``` Il decoratore `@Resolver(() => User)` stabilisce il contesto per i decoratori `@ResolveField()`, abilitando la risoluzione a livello di campo per dati calcolati o correlati. ## Tipi di Input e Validazione con class-validator I tipi di input GraphQL definiscono i payload delle mutation. La combinazione di `@InputType()` con i decoratori [class-validator](https://github.com/typestack/class-validator) abilita la validazione sia a livello di schema che a runtime. ```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; } ``` La validazione viene abilitata globalmente aggiungendo la `ValidationPipe` in `main.ts`. Gli errori GraphQL includono i messaggi di validazione nel campo `extensions`, mantenendo la compatibilità con i client. ## Risolvere Dati Correlati con @ResolveField e DataLoader I field resolver gestiscono le relazioni tra tipi. Senza ottimizzazione, il recupero di una lista di utenti con i loro post attiva N+1 query—una per gli utenti, poi una per utente per i post. ```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 raggruppa e memorizza nella cache le richieste all'interno di una singola operazione GraphQL. Per NestJS, il DataLoader viene fornito a livello di request utilizzando `@Injectable({ scope: Scope.REQUEST })`. ```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 riduce le query N+1 a una singola query raggruppata, cruciale per le API GraphQL dove i client controllano la profondità delle query. Per una copertura più approfondita dei pattern architetturali NestJS, consultare le [domande da colloquio su NestJS Module e Dependency Injection](/technologies/node-nestjs/interview-questions/nestjs-modules-di). ## Subscription per Dati in Tempo Reale Le subscription GraphQL inviano dati ai client tramite connessioni WebSocket. NestJS utilizza la libreria `graphql-ws`, che implementa il [protocollo GraphQL over WebSocket](https://github.com/enisdenjo/graphql-ws). ```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; } } ``` Per deployment in produzione su più istanze, sostituire la `PubSub` in-memory con `graphql-redis-subscriptions` per trasmettere eventi a livello di cluster. ## Autenticazione e Autorizzazione in GraphQL I Guard di NestJS funzionano perfettamente con i resolver GraphQL. Il contesto di esecuzione differisce da REST—utilizzare `GqlExecutionContext` per estrarre la request. ```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 } } ``` I guard vengono applicati a livello di resolver o metodo. L'autorizzazione a livello di campo utilizza `@ResolveField()` con logica condizionale basata sui ruoli utente. ```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; } } ``` Per autorizzazioni complesse, considerare [GraphQL Shield](https://github.com/dimatill/graphql-shield) o l'integrazione CASL integrata in NestJS. L'articolo [NestJS Guard e Interceptor](/blog/node-nestjs/nestjs-guards-interceptors-modular-architecture) tratta questi pattern in dettaglio. ## Domande Comuni da Colloquio su NestJS GraphQL I colloqui tecnici per posizioni NestJS includono frequentemente domande specifiche su GraphQL. Ecco i pattern che i responsabili delle assunzioni valutano. **D: Come gestisce NestJS il problema N+1 in GraphQL?** DataLoader raggruppa le chiamate ai field resolver all'interno di una singola richiesta. Quando più oggetti padre richiedono lo stesso campo, DataLoader raccoglie tutte le chiavi, esegue una query raggruppata e distribuisce i risultati. Il loader deve essere request-scoped per evitare problemi di caching tra le richieste. **D: Qual è la differenza tra code-first e schema-first in NestJS GraphQL?** Code-first genera lo schema GraphQL dai decoratori TypeScript a runtime, mantenendo tipi e schema sincronizzati automaticamente. Schema-first analizza file SDL `.graphql`, richiedendo definizioni di tipo manuali. Code-first è adatto a team nativi TypeScript; schema-first funziona meglio quando lo schema è il contratto tra team frontend e backend. **D: Come implementare permessi a livello di campo?** Esistono tre approcci: (1) `@ResolveField()` con ritorni condizionali basati sul contesto utente, (2) Decoratori personalizzati che verificano i permessi prima della risoluzione del campo, (3) Direttive schema come `@auth(requires: ADMIN)` elaborate da un trasformatore di direttive. Il primo approccio offre la massima flessibilità; le direttive forniscono la documentazione dello schema più pulita. **D: Spiega il contesto GraphQL in NestJS.** L'oggetto contesto passa attraverso tutti i resolver all'interno di una richiesta. NestJS lo popola con la richiesta HTTP di default. Il contesto personalizzato viene configurato in `GraphQLModule.forRoot()` tramite l'opzione `context`—utile per aggiungere istanze DataLoader, utente autenticato o connessioni database. **D: Come scalano le subscription su più istanze server?** PubSub in-memory funziona solo per deployment a istanza singola. Architetture multi-istanza richiedono un broker esterno—Redis PubSub è lo standard. Ogni istanza server sottoscrive i canali Redis; quando un'istanza pubblica un evento, tutte le istanze lo ricevono e lo inviano ai client WebSocket connessi. Per ulteriore preparazione ai colloqui NestJS, esplorare il modulo [Middleware e Interceptor](/technologies/node-nestjs/interview-questions/middleware-interceptors) che copre i pattern del ciclo di vita delle richieste. ## Conclusione - NestJS GraphQL supporta sia l'approccio code-first che schema-first—code-first semplifica i progetti TypeScript, schema-first è adatto ai workflow guidati da SDL - DataLoader elimina le query N+1 raggruppando le richieste dei field resolver all'interno di una singola operazione - Le istanze DataLoader request-scoped prevengono l'inquinamento della cache tra richieste concorrenti - `GqlExecutionContext` collega i Guard NestJS con il contesto dei resolver GraphQL - Le subscription in produzione richiedono Redis PubSub per la distribuzione di eventi multi-istanza - L'autorizzazione a livello di campo combina `@ResolveField()` con controlli del contesto utente --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/node-nestjs/nestjs-graphql-schemas-resolvers-tutorial