O que é GraphQL?
GraphQL é uma linguagem de consulta e runtime para APIs. Em vez de expor diversos endpoints fixos, um servidor GraphQL expõe um único schema tipado, e os clientes enviam queries que selecionam exatamente os campos de que precisam. O servidor resolve cada campo e retorna uma resposta moldada precisamente como a requisição.
O Facebook o criou em 2012 para resolver um problema em dispositivos móveis: os endpoints REST retornavam dados demais ou de menos, e buscar todas as informações relacionadas de uma tela exigia muitas requisições (round trips). O GraphQL resolveu ambos os problemas permitindo que o cliente descreva seus requisitos de dados em um único documento.
O schema
O schema é o contrato. Ele define os tipos e as operações disponíveis.
# schema.graphql
type User {
id: ID!
name: String!
email: String
posts(first: Int = 10): [Post!]!
}
type Post {
id: ID!
title: String!
body: String!
author: User!
}
type Query {
user(id: ID!): User
posts(limit: Int): [Post!]!
}
type Mutation {
createPost(input: CreatePostInput!): Post!
}
O ! marca um campo como não nulo. Os tipos se compõem em um grafo, e é por isso que uma única query pode navegar de um usuário para seus posts e voltar para os autores. O schema é introspectável, permitindo que as ferramentas gerem documentação, tipos e autocompletar automaticamente.
Queries
Uma query seleciona campos do tipo de query raiz, utilizando argumentos e variáveis.
# posts.graphql
query RecentPosts($limit: Int!) {
posts(limit: $limit) {
id
title
author {
name
}
}
}
A resposta reflete a seleção exatamente: sem campos extras e sem omissões. Variáveis tornam as queries reutilizáveis e permitem que o servidor valide os tipos dos argumentos. Fragments extraem seleções repetidas para reutilização.
# fragment.graphql
fragment PostCard on Post {
id
title
author { name }
}
query Feed {
posts { ...PostCard }
}
Mutations e subscriptions
Uma mutation altera dados e retorna um formato definido, geralmente incluindo o objeto atualizado para que o cliente possa atualizar seu cache.
# create.graphql
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
}
}
Uma subscription transmite atualizações em tempo real através de uma conexão persistente, como novas mensagens ou notificações ao vivo. Use-a quando o servidor precisar enviar dados para o cliente; para atualizações ocasionais, polling ou um refetch são opções mais simples.
Resolvers
Os resolvers são onde o schema encontra os seus dados. Cada campo possui um resolver que retorna o seu valor.
// resolvers.js
const resolvers = {
Query: {
user: (_parent, { id }, { db }) => db.users.findById(id),
posts: (_parent, { limit = 10 }, { db }) => db.posts.findMany({ limit }),
},
User: {
posts: (user, { first }, { db }) =>
db.posts.findMany({ where: { authorId: user.id }, limit: first }),
},
Mutation: {
createPost: (_parent, { input }, { db }) => db.posts.create(input),
},
};
O objeto de contexto carrega recursos compartilhados, como o banco de dados e o usuário autenticado. Os resolvers devem ser pequenos, e as verificações de autorização devem ser feitas aqui ou na camada de serviço que eles chamam — nunca no cliente.
O problema N+1
A armadilha de performance mais comum no GraphQL são as queries N+1. Se uma query retorna dez posts e o resolver do autor de cada post faz uma chamada ao banco de dados, você executará onze queries.
O DataLoader resolve isso agrupando (batching) e fazendo o cache de chamadas dentro de uma única requisição.
// loaders.js
import DataLoader from "dataloader";
function createUserLoader(db) {
return new DataLoader(async (ids) => {
const users = await db.users.findByIds(ids);
return ids.map((id) => users.find((u) => u.id === id));
});
}
O resolver do autor então chama loaders.user.load(post.authorId), e o DataLoader coleta todos os carregamentos do tick em uma única query. Esta é a otimização mais importante em um servidor GraphQL.
Clientes e caching
Bibliotecas de cliente como Apollo Client e urql oferecem caching, normalização, estados de carregamento e erro, além de integração com frameworks de UI. Um cache normalizado armazena entidades por tipo e id, portanto, uma mutation que retorna um post atualizado atualiza automaticamente todas as queries que fazem referência a ele.
Como o GraphQL geralmente utiliza um único endpoint POST, o caching de HTTP é menos eficaz do que no REST. Os clientes compensam isso com caches normalizados, e os servidores utilizam persisted queries e caching de resposta. Projete suas mutations para retornar os objetos alterados, para que o cache do cliente permaneça consistente.
GraphQL ou REST?
O GraphQL se destaca quando:
- Os clientes precisam de campos diferentes para telas diferentes.
- Uma tela precisa de dados de muitos recursos relacionados.
- Você deseja uma API única, tipada e autodocumentada.
O REST é mais simples quando:
- Os recursos mapeiam de forma clara para endpoints.
- O cache HTTP e os códigos de status são importantes.
- A API é pequena e estável.
Muitas equipes utilizam ambos, usando GraphQL para a API do produto e REST para webhooks, upload de arquivos e endpoints públicos simples.
Melhores práticas
- Projete o schema com base nas necessidades do cliente, não nas tabelas do banco de dados.
- Use tipos não nulos de forma deliberada e versione o schema utilizando deprecations.
- Mantenha os resolvers enxutos e mova a lógica para services.
- Resolva o problema de N+1 com DataLoader desde o início.
- Adicione limites de profundidade (depth) e complexidade de query para evitar abusos.
- Retorne os objetos alterados em mutations para garantir a consistência do cache.
- Use persisted queries em produção para reduzir o tamanho do payload.
Erros comuns
- Expor o schema do banco de dados diretamente como o schema do GraphQL.
- Ignorar o problema de N+1 até que a performance colapse.
- Buscar todos os dados em cada query, perdendo o principal benefício.
- Confiar na autorização fornecida pelo cliente ou em permissões de nível de campo.
- Construir queries profundamente aninhadas sem um limite de profundidade.
- Assumir que o cache HTTP funciona da mesma forma que no REST.
Próximos passos
GraphQL é uma maneira poderosa de moldar APIs com foco nos clientes. Implemente-a sobre o protocolo HTTP, construa o servidor com Node.js e consuma-o a partir do React utilizando uma biblioteca de cliente. Em seguida, modele uma tela que você conheça bem como um schema e uma query, e veja como os requisitos de dados se mapeiam de forma natural.