🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

Como construir uma API GraphQL com Node.js e Apollo Server em 2026

⏱️4 min read  ·  803 words

GráficoQL permite que os clientes solicitem exatamente os dados de que precisam em uma única solicitação — sem busca excessiva, sem busca insuficiente. Combinado comServidor Apolo e TypeScript, é uma maneira poderosa de criar APIs flexíveis. Este guia cria uma API GraphQL completa do zero.

GraphQL vs REST — Comparação Rápida

Aspecto DESCANSO GráficoQL
Busca de dados Vários pontos de extremidade Endpoint único, consultas precisas
Busca excessiva Comum Eliminado
Versionamento Versões de URL (v1, v2) Evolução do esquema
Melhor para CRUD simples, cache Dados complexos, aninhados e orientados ao cliente

Configuração do Projeto

mkdir graphql-api && cd graphql-api
npm init -y
npm install @apollo/server graphql
npm install -D typescript tsx @types/node
npx tsc --init

Defina o esquema

// src/schema.ts
export const typeDefs = `#graphql
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
    published: Boolean!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
    posts: [Post!]!
  }

  type Mutation {
    createUser(name: String!, email: String!): User!
    createPost(title: String!, content: String!, authorId: ID!): Post!
    publishPost(id: ID!): Post!
  }
`;

Resolvedores de gravação

// src/resolvers.ts
import { db } from './db';

export const resolvers = {
  Query: {
    users: () => db.users.findAll(),
    user: (_: any, { id }: { id: string }) => db.users.findById(id),
    posts: () => db.posts.findAll(),
  },

  Mutation: {
    createUser: (_: any, { name, email }: { name: string; email: string }) => {
      return db.users.create({ name, email });
    },
    createPost: (_: any, args: { title: string; content: string; authorId: string }) => {
      return db.posts.create({ ...args, published: false });
    },
    publishPost: (_: any, { id }: { id: string }) => {
      return db.posts.update(id, { published: true });
    },
  },

  // Field resolvers for relationships
  User: {
    posts: (parent: { id: string }) => db.posts.findByAuthor(parent.id),
  },
  Post: {
    author: (parent: { authorId: string }) => db.users.findById(parent.authorId),
  },
};

Crie o Servidor

// src/index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { typeDefs } from './schema';
import { resolvers } from './resolvers';

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

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
  context: async ({ req }) => {
    // Extract auth token, attach user to context
    const token = req.headers.authorization?.replace('Bearer ', '');
    const user = token ? await verifyToken(token) : null;
    return { user };
  },
});

console.log("GraphQL ready at " + url);

Autenticação em Resolvedores

Mutation: {
  createPost: (_: any, args, context) => {
    // Require authentication
    if (!context.user) {
      throw new GraphQLError('Not authenticated', {
        extensions: { code: 'UNAUTHENTICATED' },
      });
    }
    return db.posts.create({ ...args, authorId: context.user.id });
  },
},

Resolvendo o problema N+1 com DataLoader

Os resolvedores de campo podem acionar consultas N+1 (uma consulta por pai). O DataLoader agrupa e armazena em cache:

npm install dataloader
import DataLoader from 'dataloader';

// Create a loader that batches user lookups
const userLoader = new DataLoader(async (ids: readonly string[]) => {
  const users = await db.users.findByIds(ids);
  // Return in the same order as requested ids
  return ids.map(id => users.find(u => u.id === id));
});

// Use in resolver — batches all author lookups into one query
Post: {
  author: (parent, _, context) => context.userLoader.load(parent.authorId),
},

Consulta de exemplo

# Client requests exactly what it needs
query {
  user(id: "1") {
    name
    posts {
      title
      published
    }
  }
}

# Response contains only requested fields:
# { "data": { "user": { "name": "Alice", "posts": [...] } } }

Perguntas Frequentes

P: GraphQL ou REST para meu projeto?
R: GraphQL para dados complexos, aninhados e orientados ao cliente (aplicativos móveis, painéis com necessidades variadas). REST para CRUD simples e quando o cache HTTP é mais importante. Muitas equipes usam ambos.

P: Como lidar com uploads de arquivos no GraphQL?
R: Use o pacote graphql-upload, ou melhor, carregue arquivos para armazenamento de objetos (S3) por meio de um endpoint REST separado ou URL pré-assinado e passe o URL em sua mutação GraphQL.

P: O GraphQL é mais difícil de armazenar em cache do que o REST?
R: Sim no nível HTTP (endpoint único, solicitações POST). Use o cache normalizado do Apollo Client no cliente e consultas persistentes ou cache de resposta no servidor para compensar.

P: Como posso evitar consultas caras/maliciosos?
R: Adicione limitação de profundidade de consulta, análise de complexidade e limitação de taxa. Pacotes como graphql-profundidade-limit e graphql-query-complexity protegem contra consultas abusivas profundamente aninhadas.

P: Devo usar o código primeiro ou o esquema primeiro?
R: O esquema primeiro (strings SDL, mostrados aqui) é claro e independente de linguagem. Code-first (Nexus, TypeGraphQL) oferece melhor integração com TypeScript. Ambos funcionam; escolha com base na preferência da equipe.

Conclusão

GraphQL com Apollo Server oferece aos clientes controle preciso sobre os dados que buscam, eliminando a busca excessiva e insuficiente. A configuração aqui – esquema digitado, resolvedores com relacionamentos, autenticação baseada em contexto e DataLoader para o problema N+1 – cobre o essencial de uma API GraphQL de produção. Adicione limitação de profundidade e análise de complexidade antes de entrar no ar e emparelhe com o Apollo Client para obter um cache normalizado no frontend.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা