NestJS e GraphQL em 2026: Schemas, Resolvers e Perguntas de Entrevista
Guia completo sobre a integração do NestJS com GraphQL: abordagens code-first e schema-first, criação de resolvers, e preparação para entrevistas técnicas em 2026.

A integração do GraphQL no NestJS oferece uma abordagem estruturada para construir APIs type-safe que aproveitam os decorators do TypeScript e a linguagem de consulta GraphQL. Este tutorial aborda as abordagens schema-first e code-first, os padrões de resolvers, e as perguntas de entrevista feitas pelos recrutadores em 2026.
O NestJS 11 utiliza por padrão a abordagem code-first, gerando o schema a partir de classes TypeScript. A abordagem schema-first permanece disponível para equipes com arquivos .graphql existentes ou workflows baseados em SDL.
Configuração do NestJS GraphQL com Apollo Server
O NestJS se integra com o Apollo Server através do pacote @nestjs/graphql. A configuração difere conforme a abordagem escolhida: code-first gera o SDL a partir dos decorators, enquanto schema-first faz o parse diretamente dos arquivos .graphql.
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'),
sortSchema: true,
playground: process.env.NODE_ENV !== 'production',
introspection: process.env.NODE_ENV !== 'production',
}),
],
})
export class AppModule {}A opção autoSchemaFile ativa o modo code-first. Defini-la como true gera o schema em memória sem escrever no disco, o que é útil para deploys serverless onde o acesso ao sistema de arquivos pode estar restrito.
Definição de Tipos GraphQL com Decorators Code-First
A abordagem code-first define os tipos GraphQL usando classes TypeScript decoradas com @ObjectType(). Cada campo utiliza @Field() para especificar seu tipo GraphQL e sua nulabilidade.
import { ObjectType, Field, ID, Int } from '@nestjs/graphql';
@ObjectType({ description: 'Usuário da aplicação' })
export class User {
@Field(() => ID)
id: string;
@Field()
email: string;
@Field({ nullable: true })
displayName?: string;
@Field(() => Int, { defaultValue: 0 })
postCount: number;
@Field(() => [Post], { nullable: 'itemsAndList' })
posts?: Post[];
// Campos sem @Field() são excluídos do schema GraphQL
passwordHash: string;
}A opção nullable aceita três valores: true (campo opcional), 'items' (os itens da lista podem ser null), e 'itemsAndList' (tanto a lista quanto os itens podem ser null). Essa granularidade corresponde precisamente à semântica de nulabilidade do GraphQL.
Construção de Resolvers para Queries e Mutations
Os resolvers tratam as operações GraphQL de entrada. O NestJS utiliza @Resolver() para marcar uma classe como resolver e @Query() ou @Mutation() para definir as operações.
import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql';
import { User } from './user.entity';
import { UserService } from './user.service';
import { CreateUserInput } from './dto/create-user.input';
@Resolver(() => User)
export class UserResolver {
constructor(private readonly userService: UserService) {}
@Query(() => [User], { name: 'users' })
async findAll(): Promise<User[]> {
return this.userService.findAll();
}
@Query(() => User, { name: 'user', nullable: true })
async findOne(@Args('id', { type: () => ID }) id: string): Promise<User | null> {
return this.userService.findById(id);
}
@Mutation(() => User)
async createUser(@Args('input') input: CreateUserInput): Promise<User> {
return this.userService.create(input);
}
}O primeiro argumento de @Query() e @Mutation() define o tipo de retorno GraphQL. A opção name personaliza o nome do campo no schema, útil quando as convenções de nomenclatura do TypeScript diferem dos padrões do GraphQL.
Criação de Input Types para as Mutations
As mutations requerem input types para validar e tipar os dados de entrada. O NestJS utiliza @InputType() para definir essas estruturas.
import { InputType, Field } from '@nestjs/graphql';
import { IsEmail, MinLength, IsOptional } from 'class-validator';
@InputType()
export class CreateUserInput {
@Field()
@IsEmail({}, { message: 'Formato de email inválido' })
email: string;
@Field()
@MinLength(8, { message: 'A senha deve ter pelo menos 8 caracteres' })
password: string;
@Field({ nullable: true })
@IsOptional()
displayName?: string;
}A integração com class-validator permite validação declarativa. O ValidationPipe do NestJS aplica automaticamente essas regras antes da execução do resolver.
Gerenciamento de Relações com Field Resolvers
Os field resolvers resolvem campos específicos de forma lazy, evitando o carregamento de dados não solicitados. Essa técnica implementa o padrão N+1 com DataLoader.
import { Resolver, ResolveField, Parent } from '@nestjs/graphql';
import { User } from './user.entity';
import { Post } from '../post/post.entity';
import { PostService } from '../post/post.service';
@Resolver(() => User)
export class UserResolver {
constructor(private readonly postService: PostService) {}
@ResolveField(() => [Post])
async posts(@Parent() user: User): Promise<Post[]> {
return this.postService.findByUserId(user.id);
}
@ResolveField(() => Int)
async postCount(@Parent() user: User): Promise<number> {
return this.postService.countByUserId(user.id);
}
}O decorator @Parent() injeta o objeto pai resolvido. Essa abordagem permite resolução condicional baseada nos campos solicitados pelo cliente.
Otimização com DataLoader para Evitar o Problema N+1
O problema N+1 ocorre quando os field resolvers executam uma consulta por elemento pai. O DataLoader agrupa essas consultas em uma única operação batch.
import * as DataLoader from 'dataloader';
import { Injectable, Scope } from '@nestjs/common';
import { PostService } from './post.service';
import { Post } from './post.entity';
@Injectable({ scope: Scope.REQUEST })
export class PostLoader {
constructor(private readonly postService: PostService) {}
readonly batchByUserId = new DataLoader<string, Post[]>(async (userIds) => {
const posts = await this.postService.findByUserIds([...userIds]);
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) || []);
});
}O uso de Scope.REQUEST garante que uma nova instância do DataLoader seja criada para cada requisição, evitando vazamentos de cache entre usuários.
Autenticação e Autorizações no GraphQL
A proteção dos resolvers utiliza os guards do NestJS combinados com decorators personalizados para extrair informações de autenticação.
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
@Injectable()
export class GqlAuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();
return !!req.user;
}
}
// current-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
export const CurrentUser = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req.user;
},
);O guard converte o contexto de execução padrão em contexto GraphQL através de GqlExecutionContext.create(). Essa conversão é necessária porque o GraphQL utiliza uma estrutura de contexto diferente do REST.
Tratamento de Erros e Exceções GraphQL
O GraphQL trata os erros de forma diferente do REST. As exceções são transformadas em erros GraphQL estruturados com extensões personalizáveis.
import { Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { GqlExceptionFilter, GqlArgumentsHost } from '@nestjs/graphql';
import { GraphQLError } from 'graphql';
@Catch(HttpException)
export class GraphqlExceptionFilter implements GqlExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const gqlHost = GqlArgumentsHost.create(host);
const response = exception.getResponse();
return new GraphQLError(
typeof response === 'string' ? response : (response as any).message,
{
extensions: {
code: this.getErrorCode(exception.getStatus()),
status: exception.getStatus(),
},
},
);
}
private getErrorCode(status: number): string {
const codes: Record<number, string> = {
400: 'BAD_REQUEST',
401: 'UNAUTHENTICATED',
403: 'FORBIDDEN',
404: 'NOT_FOUND',
};
return codes[status] || 'INTERNAL_SERVER_ERROR';
}
}Essa abordagem mantém a compatibilidade com clientes GraphQL padrão que esperam erros estruturados conforme a especificação GraphQL.
Subscriptions para Atualizações em Tempo Real
As subscriptions do GraphQL permitem comunicações em tempo real via WebSocket. O NestJS utiliza graphql-subscriptions para pub/sub.
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
subscriptions: {
'graphql-ws': true,
'subscriptions-transport-ws': false,
},
}),
],
})
export class AppModule {}
// notification.resolver.ts
import { Resolver, Subscription, Mutation, Args } from '@nestjs/graphql';
import { PubSub } from 'graphql-subscriptions';
const pubSub = new PubSub();
@Resolver()
export class NotificationResolver {
@Subscription(() => String, {
filter: (payload, variables) => payload.userId === variables.userId,
})
notificationReceived(@Args('userId') userId: string) {
return pubSub.asyncIterableIterator('NOTIFICATION_RECEIVED');
}
@Mutation(() => Boolean)
async sendNotification(
@Args('userId') userId: string,
@Args('message') message: string,
) {
await pubSub.publish('NOTIFICATION_RECEIVED', {
notificationReceived: message,
userId,
});
return true;
}
}O protocolo graphql-ws é o padrão moderno para subscriptions GraphQL, substituindo o antigo subscriptions-transport-ws que não recebe mais manutenção.
Pronto para mandar bem nas entrevistas de Node.js / NestJS?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Perguntas de Entrevista NestJS GraphQL em 2026
As entrevistas técnicas avaliam a compreensão dos conceitos GraphQL e sua aplicação no NestJS. Estas são as perguntas frequentes.
Qual é a diferença entre code-first e schema-first?
Code-first gera o schema SDL a partir dos decorators TypeScript, oferecendo tipagem end-to-end automática. Schema-first faz o parse de arquivos .graphql e gera tipos TypeScript via plugins. Code-first é adequado para novas aplicações TypeScript, enquanto schema-first se adapta a projetos com um schema GraphQL existente ou equipes familiarizadas com SDL.
Como resolver o problema N+1 no GraphQL?
O problema N+1 se resolve com DataLoader, que agrupa consultas individuais em operações batch. Cada DataLoader deve ter escopo de requisição (Scope.REQUEST) para evitar vazamentos de cache. A implementação batch deve retornar resultados na ordem exata das chaves de entrada.
Como proteger uma API GraphQL no NestJS?
A segurança combina múltiplas camadas: guards para autenticação no nível do resolver, interceptors para transformação de respostas, class-validator para validação de inputs, e throttling para proteção contra abusos. A introspection e o playground devem ser desabilitados em produção.
Qual é a diferença entre Query, Mutation e Subscription?
Query lê dados sem efeitos colaterais. Mutation modifica dados no servidor. Subscription estabelece uma conexão WebSocket para atualizações em tempo real. O GraphQL garante que as queries são idempotentes, enquanto as mutations podem ter efeitos colaterais.
Como tratar erros no GraphQL NestJS?
Os erros GraphQL utilizam a classe GraphQLError com extensões personalizadas. Os exception filters convertem as exceções do NestJS em erros GraphQL estruturados. Os erros parciais permitem retornar dados válidos mesmo que alguns campos falhem.
Conclusão
A integração do NestJS com GraphQL fornece um framework robusto para construir APIs type-safe e de alto desempenho. A abordagem code-first simplifica o desenvolvimento ao eliminar a duplicação entre as definições TypeScript e o schema GraphQL. Padrões como DataLoader e os guards de autenticação garantem aplicações escaláveis e seguras. O domínio desses conceitos, combinado com uma compreensão dos trade-offs entre as diferentes abordagens, prepara efetivamente para as entrevistas técnicas de Node.js em 2026.
Compartilhar
Artigos relacionados

NestJS e TypeORM em 2026: migrations, relações e perguntas de entrevista
Dominar a integração do NestJS com o TypeORM usando as migrations do TypeORM 1.0, as relações entre entidades, o padrão repository e as perguntas de entrevista comuns para desenvolvedores backend.

Node.js 24 em 2026: URLPattern, Permission Model e perguntas de entrevista
O Node.js 24 LTS traz um Permission Model estável, URLPattern global, gerenciamento explícito de recursos com using/await using e V8 13.6. Uma análise aprofundada dos recursos que importam em produção e em entrevistas.

Microsserviços com NestJS em 2026: Arquitetura gRPC, Padrões de Streaming e Perguntas de Entrevista
Guia prático sobre arquitetura de microsserviços NestJS com gRPC: camadas de transporte, Protocol Buffers, padrões de streaming e perguntas de entrevista para desenvolvedores backend em 2026.