Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Apollo Server no Node.js: Guia Prático

Atualizado em: 1 de setembro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

Usar Apollo Server no Node.js permite criar APIs GraphQL com schema, resolvers, context, plugins, cache e integrações com frameworks web. O servidor é compatível com clientes GraphQL e pode funcionar como API única, subgraph de Federation ou camada de composição sobre bancos e serviços.

Uma implementação de produção precisa ir além do exemplo inicial. É necessário autenticar no context, aplicar autorização em resolvers, limitar profundidade e complexidade, evitar N+1, configurar CORS e CSRF, padronizar erros, drenar o servidor no shutdown e observar operações.

Neste guia, você aprenderá a iniciar Apollo Server, definir schema, criar resolvers, integrar com Express, usar DataLoader, validar acesso, aplicar plugins, tratar erros, testar e preparar o deploy.

O que é Apollo Server?

Apollo Server é um servidor GraphQL open source para Node.js. A documentação oficial do Apollo Server apresenta as versões atuais e integrações. A especificação GraphQL define linguagem, validação e execução.

Quando usar?

  • API GraphQL central;
  • BFF para web ou mobile;
  • subgraph federado;
  • composição de bancos e REST;
  • migração gradual de APIs;
  • portal interno de dados.

Instalando

npm install @apollo/server graphql

Para Express, instale também a integração e os middlewares necessários conforme a versão.

Schema

const typeDefs = `#graphql
  type User {
    id: ID!
    name: String!
    email: String!
  }

  type Query {
    user(id: ID!): User
  }
`;

O schema é o contrato público. Campos nullable e non-null possuem impacto no tratamento de erros.

Resolvers

const resolvers = {
  Query: {
    user: (_, { id }, context) => {
      return context.repositories.users.findById({
        tenantId: context.auth.tenantId,
        userId: id
      });
    }
  }
};

Resolvers devem ser pequenos e delegar regras a serviços e repositories.

Iniciando standalone

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

const server = new ApolloServer({
  typeDefs,
  resolvers
});

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
  context: async ({ req }) => createContext(req)
});

Standalone é útil para projetos simples. Integrações com framework oferecem mais controle de middleware, cookies e rotas.

Integração com Express

import express from 'express';
import cors from 'cors';
import { json } from 'body-parser';
import { expressMiddleware } from '@apollo/server/express4';

await server.start();

app.use(
  '/graphql',
  cors(corsOptions),
  json({ limit: '1mb' }),
  expressMiddleware(server, {
    context: async ({ req }) => createContext(req)
  })
);

Confirme o pacote de integração indicado pela versão atual.

Context

async function createContext(req) {
  const auth = await authenticate(req);

  return {
    auth,
    repositories,
    services,
    loaders: createLoaders({ auth, repositories }),
    requestId: req.headers['x-request-id']
  };
}

Crie um novo context por operação.

Não coloque estado global

Usuário, tenant e loaders não podem ficar em variáveis globais, pois requisições executam concorrentemente.

Autenticação

Valide token ou sessão antes de executar operações protegidas. Um context pode representar usuário anônimo, mas resolvers precisam exigir identidade quando necessário.

Consulte JWT Seguro no Node.js e Sessões Seguras no Node.js.

Autorização

function requirePermission(context, permission) {
  if (!context.auth.permissions.includes(permission)) {
    throw new ForbiddenError();
  }
}

Não confie na ausência do campo na interface. A regra deve estar no backend.

Veja RBAC no Node.js e ABAC no Node.js.

Autorização por objeto

Uma permission geral não garante acesso ao recurso. Inclua tenant e ownership na consulta.

Multi-tenancy

O tenant vem da identidade, não de argumento livre. Consulte Multi-Tenancy no Node.js.

N+1

Resolvers de campos relacionados podem executar uma consulta por item. Use DataLoader no Node.js.

Loader por request

function createLoaders({ auth, repositories }) {
  return {
    user: createUserLoader({
      tenantId: auth.tenantId,
      repository: repositories.users
    })
  };
}

Uma instância global pode vazar cache entre usuários.

Mutations

type Mutation {
  updateUser(id: ID!, input: UpdateUserInput!): User!
}

Valide input, autorize e execute a transação na camada de serviço.

Input types

Não reutilize tipos de output como input. Defina campos permitidos e evite mass assignment.

Validação

GraphQL valida tipos básicos, mas regras como tamanho, formato e combinação de campos precisam de validação adicional.

Custom scalars

Use scalars para DateTime, URL e outros tipos quando houver implementação bem testada. Não aceite qualquer string sem limite.

Erros

GraphQL pode retornar HTTP 200 com campo errors. Padronize códigos:

{
  "errors": [{
    "message": "Acesso negado",
    "extensions": {
      "code": "FORBIDDEN"
    }
  }]
}

Não exponha stack

Em produção, remova detalhes internos, SQL, hosts e stack. Preserve request ID para suporte.

formatError

Use para sanitização cuidadosa, sem esconder erros úteis de validação.

Null propagation

Se um campo non-null falha, o erro pode propagar e tornar o objeto pai null. Modele nullability conforme o domínio.

Partial data

GraphQL permite dados parciais com erros. Documente quais campos podem falhar independentemente.

Queries complexas

Um cliente pode criar query profunda e cara. Limite:

  • profundidade;
  • quantidade de aliases;
  • complexidade;
  • tamanho do body;
  • batching HTTP;
  • tempo de execução.

Depth limit

Analise o documento validado e rejeite operações acima do limite. Profundidade sozinha não mede custo.

Complexidade

Atribua custo maior a listas e campos caros. Considere argumentos como first.

Paginação

Listas devem ter limite e cursor. Consulte Paginação em APIs Node.js.

Introspection

Introspection é útil para ferramentas. Em APIs privadas, autentique ou restrinja conforme o risco. Desabilitar não substitui segurança.

Landing page

Configure a interface de exploração de acordo com o ambiente. Produção pode exigir autenticação ou ausência de landing page pública.

CSRF

Apollo Server possui mecanismos de prevenção, mas cookies e integrações exigem configuração de CORS, métodos e headers.

CORS

Use allowlist de origens. Consulte CORS em APIs Node.js.

GET para queries

GET permite cache e persisted queries, mas URLs podem aparecer em logs. Não coloque dados sensíveis em argumentos.

Automatic Persisted Queries

O cliente envia hash da operação e, quando necessário, o documento. Isso reduz payload, mas precisa de cache e limites.

Allowlist de operações

Aplicações controladas podem permitir apenas operações conhecidas. Isso reduz queries arbitrárias, mas exige processo de publicação.

Cache

Apollo pode fornecer hints de cache, mas dados privados precisam de políticas adequadas. Não cacheie resposta de um usuário para outro.

Response cache

Inclua identidade e tenant na chave quando a resposta é personalizada. Considere invalidation.

Data sources

Encapsule clientes REST, bancos e serviços. Defina timeout, retry e circuit breaker.

RESTDataSource

Uma fonte REST pode centralizar headers, cache e tratamento de erros. Não encaminhe Authorization externo indiscriminadamente.

Timeouts

Cada downstream precisa de deadline menor que o prazo da operação.

AbortController

Quando o cliente desconecta, propague cancelamento quando a integração permitir.

Subscriptions

Subscriptions exigem transporte, autenticação, reconexão e autorização por evento. Não assuma que o usuário permanece autorizado para sempre.

WebSocket

Consulte WebSocket no Node.js. Valide token na conexão e antes de eventos sensíveis.

Plugins

Plugins observam lifecycle:

  • request;
  • parsing;
  • validation;
  • execution;
  • response;
  • shutdown.

Plugin de logging

const loggingPlugin = {
  async requestDidStart(requestContext) {
    const startedAt = performance.now();

    return {
      async willSendResponse(ctx) {
        logger.info({
          operationName: ctx.request.operationName,
          durationMs: performance.now() - startedAt
        }, 'Operação GraphQL concluída');
      }
    };
  }
};

Não registre variables completas.

Operation name

Exija nomes em produção quando possível. Eles melhoram métricas e allowlists.

Cardinalidade

Não use a query completa como label de métrica. Use operation name controlado e status.

Tracing

Crie span para operação e downstreams. Evite span por cada campo simples.

Métricas

Monitore:

  • operações por nome;
  • latência;
  • erros;
  • complexidade rejeitada;
  • N+1;
  • downstreams;
  • cache hit;
  • subscriptions ativas.

Health checks

Separe liveness e readiness. A aplicação pode estar viva, mas incapaz de atender por falta de banco ou schema.

Graceful shutdown

Use plugin de drain do HTTP server ou mecanismo da integração. Pare novas requisições e aguarde operações em andamento.

Consulte Graceful Shutdown no Node.js.

Schema modular

Divida typeDefs e resolvers por domínio. Evite um arquivo único com milhares de linhas.

Code generation

Ferramentas podem gerar tipos TypeScript a partir do schema e operações. Isso reduz divergência, mas não substitui validação runtime.

Schema-first

O contrato SDL orienta implementação e clientes. Mudanças breaking precisam de processo.

Code-first

Bibliotecas criam schema a partir de código e decorators. Avalie transparência do schema gerado.

Breaking changes

Remover campo, tornar nullable em non-null ou mudar tipo pode quebrar clientes. Use depreciação e métricas de uso.

Deprecation

oldField: String @deprecated(
  reason: "Use newField"
)

Não remova até confirmar que clientes migraram.

Federation

Apollo Server pode funcionar como subgraph. Federação exige ownership de campos, composição, contratos e observabilidade do router.

Subgraph

Um subgraph não deve confiar cegamente em headers do gateway. Use conexão autenticada e identidade assinada ou mTLS.

Testes unitários

Teste serviços e resolvers com context explícito.

executeOperation

const response = await server.executeOperation({
  query: `query User($id: ID!) {
    user(id: $id) { id name }
  }`,
  variables: { id: '1' }
}, {
  contextValue: testContext
});

Teste de autorização

Confirme unauthenticated, forbidden, outro tenant e acesso permitido.

Teste de N+1

Conte chamadas ao repository para lista com relações. O número deve ser constante ou por batch.

Teste de complexidade

Envie query profunda, aliases repetidos e lista com limite alto. A operação deve ser rejeitada antes de executar.

Teste de erro

Simule SQL exception e confirme que stack não aparece na resposta.

Teste de shutdown

Inicie operação lenta, envie SIGTERM e confirme drain antes do encerramento.

Teste de contrato

Valide mudanças de schema no CI e bloqueie breaking changes não aprovadas.

Erros comuns

  • Loader global: dados vazam entre usuários.
  • Sem limite de query: cliente consome recursos.
  • Autorização apenas na UI: campos ficam expostos.
  • Resolver com lógica extensa: testes ficam difíceis.
  • Variables em logs: dados sensíveis aparecem.
  • Sem paginação: listas enormes são resolvidas.
  • Shutdown abrupto: operações são interrompidas.

Boas práticas

  • Use context por request.
  • Autorize no resolver e serviço.
  • Use DataLoader.
  • Limite profundidade e custo.
  • Paginação obrigatória.
  • Sanitize erros.
  • Nomeie operações.
  • Monitore por operação.
  • Teste o schema.
  • Faça graceful shutdown.

Conclusão

Usar Apollo Server no Node.js fornece uma base completa para APIs GraphQL, com integrações, plugins, context e ferramentas de produção.

A segurança e a performance dependem do desenho ao redor: autorização, DataLoader, limites, paginação, erros e observabilidade. Com schema governado e testes de complexidade, Apollo Server entrega flexibilidade sem permitir que qualquer query se torne uma operação cara ou acesse dados fora do escopo.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita