GraphQL é uma linguagem de consulta para APIs e um runtime server-side que retorna exatamente os campos que cada cliente pede em uma única requisição. A dualidade importa: a linguagem é uma especificação que qualquer cliente pode falar; o runtime executa essas queries contra um sistema de tipos que você define sobre os dados que já tem — qualquer banco, qualquer serviço. GraphQL não é um banco de dados, e não substitui nem o seu armazenamento nem, necessariamente, a sua API REST.
Principais pontos
| Pergunta | Resposta |
|---|---|
| O que é | Uma linguagem de consulta governada por spec + runtime de execução — agnóstica de armazenamento |
| O movimento característico | O formato da resposta espelha o da query: peça campos, receba esses campos |
| As três operações | query (leitura) · mutation (escrita) · subscription (push em tempo real) |
| Os blocos de construção | Schema (contrato SDL) · tipos · resolvers (funções de busca por campo) |
| A conta honesta | Estratégia de cache, batching do N+1, limites por custo, hardening de segurança |
A demo característica: query e resposta
A demo para a qual toda explicação converge, porque ela é a ideia — a resposta é a query, preenchida:
# Requisição # Resposta
{ {
post(id: "8fk2") { "data": {
title "post": {
author { "title": "Hello GraphQL",
username "author": {
} "username": "ada"
comments(first: 2) { },
text "comments": [
} { "text": "Nice." },
} { "text": "Ship it." }
} ]
}
}
}
Uma requisição, três recursos relacionados, zero campos não solicitados — o par overfetching e underfetching aposentado de um golpe só. Chamá-la de clientes reais é HTTP puro:
// JavaScript / Node.js — query Back4app's auto-generated GraphQL API
const res = await fetch('https://parseapi.back4app.com/graphql', {
method: 'POST',
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-Client-Key': CLIENT_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: '{ posts(first: 20) { edges { node { title author { username } } } } }',
}),
});
const { data } = await res.json(); // shaped exactly like the query // Flutter / Dart — query Back4app's auto-generated GraphQL API
final res = await http.post(
Uri.parse('https://parseapi.back4app.com/graphql'),
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Client-Key': clientKey,
'Content-Type': 'application/json',
},
body: jsonEncode({
'query': '{ posts(first: 20) { edges { node { title author { username } } } } }',
}),
);
final data = jsonDecode(res.body)['data']; // shaped exactly like the query // iOS / Swift — query Back4app's auto-generated GraphQL API
var request = URLRequest(url: URL(string: "https://parseapi.back4app.com/graphql")!)
request.httpMethod = "POST"
request.setValue(appId, forHTTPHeaderField: "X-Parse-Application-Id")
request.setValue(clientKey, forHTTPHeaderField: "X-Parse-Client-Key")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let query = "{ posts(first: 20) { edges { node { title author { username } } } } }"
request.httpBody = try JSONEncoder().encode(["query": query])
let (data, _) = try await URLSession.shared.data(for: request)
// data is shaped exactly like the query // Android / Kotlin — query Back4app's auto-generated GraphQL API
val body = """{ "query": "{ posts(first: 20) { edges { node { title author { username } } } } }" }"""
val request = Request.Builder()
.url("https://parseapi.back4app.com/graphql")
.addHeader("X-Parse-Application-Id", APP_ID)
.addHeader("X-Parse-Client-Key", CLIENT_KEY)
.post(body.toRequestBody("application/json".toMediaType()))
.build()
val data = client.newCall(request).execute().body?.string() // shaped like the query Schema, query, resolver: o trio que faz o trabalho
As explicações mostram a query; quase nenhuma mostra a maquinaria por trás dela como um quadro coerente. O schema é o contrato tipado, escrito em SDL:
type Post {
title: String! # ! = não nulo
author: User!
comments(first: Int): [Comment!]
}
type Query { # os pontos de entrada de leitura
post(id: ID!): Post
}
type Mutation { # os pontos de entrada de escrita
createPost(title: String!): Post!
}
Resolvers são a outra metade do runtime — uma função por campo, cada uma livre para buscar de onde quiser:
const resolvers = {
Query: {
post: (_, { id }) => db.posts.findById(id),
},
Post: {
author: (post) => db.users.findById(post.authorId), // chamada por post!
},
};
A execução é um pipeline: parsear a query, validar contra o schema (operações inválidas morrem antes de tocar em dados) e então percorrer o selection set chamando resolvers e montar o JSON espelhado. Essa chamada de resolver por campo é também o preço da flexibilidade — note o // chamada por post!, que vira o problema N+1 logo abaixo. O nome, que ninguém explica: seus dados formam um grafo de objetos tipados, e as queries o percorrem a partir dos campos raiz — mas apenas pelos caminhos expostos no schema, não em travessias arbitrárias como numa verdadeira linguagem de consulta a grafos.
Proveniência, em resumo: criado no Facebook (hoje Meta) em 2012 para seus apps mobile, aberto como open source em 2015, governado desde 2018 pela GraphQL Foundation sob a Linux Foundation, edição atual da spec de outubro de 2021, com um draft de GraphQL sobre HTTP padronizando as convenções de transporte.
Queries, mutations e subscriptions
Queries leem. Mutations escrevem — e selecionam campos no resultado, então o cliente recebe o estado pós-escrita no mesmo round trip. Subscriptions mantêm uma conexão aberta (na prática, WebSockets) e empurram eventos conforme ocorrem; são a história de tempo real do GraphQL, com a ressalva de que cada subscription ativa é estado mantido no servidor. As três compartilham o schema, o sistema de tipos e o ferramental — um contrato, três tempos verbais.
GraphQL vs. REST
| GraphQL | REST | |
|---|---|---|
| Endpoints | Um (/graphql) | Um por recurso |
| Formato da resposta | Composto pelo cliente a cada query | Fixo por endpoint |
| Over/underfetching | Resolvido na camada HTTP | Mitigado por parâmetros |
| Cache HTTP | Perdido por padrão (um único POST) | Nativo — o superpoder |
| Tipagem e introspecção | Embutidas no contrato | Opcionais via OpenAPI |
| Versionamento | Evolução sem versões + @deprecated | Convenções /v1, /v2 |
| Tempo real | Subscriptions na spec | Fora do escopo |
| Melhor em | Clientes diversos, dados aninhados | CRUD de recursos, leituras cacheáveis |
O argumento completo — inclusive quando REST é simplesmente a escolha melhor — está no verbete dedicado GraphQL vs. REST.
Rodando GraphQL em produção: os custos honestos
A seção que os explicadores de vendor suavizam. Cache: um único endpoint POST abre mão do cache HTTP e de CDN indexado por URL; a substituição são caches normalizados no cliente, indexados por id mais __typename, e persisted queries (operações pré-aprovadas e hasheadas, enviadas como GET) para recuperar parte do cache de transporte. N+1: resolvers ingênuos transformam uma lista de 20 posts em 1 + 20 leituras no banco — o mesmo problema que clientes REST têm sobre HTTP, realocado para a sua camada de resolvers e corrigido lá com loaders de batching como o DataLoader. Erros: GraphQL retorna 200 OK com um array errors — monitoramento baseado em status codes fica cego se não for reensinado. Rate limiting: requisições não são iguais quando uma query pode aninhar dez relações; APIs maduras medem o custo da query (análise de profundidade e complexidade), não a contagem de requisições. Segurança: desabilite a introspecção em produção, imponha limites de profundidade e complexidade e mantenha a autorização na camada de negócio, sob os resolvers — o endpoint único também cega regras de WAF baseadas em URL, então a validação se move para dentro da própria camada GraphQL.
Casos de uso comuns
- Apps mobile em redes limitadas — o caso de uso fundador: campos exatos, mínimo de bytes, menos round trips.
- Produtos multi-cliente — app de relógio, app de celular, dashboard web, cada um moldando as próprias respostas contra um único schema.
- Agregação backend-for-frontend — uma camada GraphQL compondo vários serviços internos para consumo pelas UIs.
- Frontends em evolução rápida — telas novas selecionam campos novos sem esperar por endpoints novos.
- Contratos tipados de ponta a ponta — a introspecção do schema gerando clientes tipados, mantendo API e UI honestas em tempo de compilação.
Você deveria usar GraphQL? Matriz de decisão
| GraphQL justifica sua maquinaria quando… | Prefira REST quando… |
|---|---|
| Clientes diferem nos dados de que precisam | Um único tipo de cliente, telas estáveis |
| Telas leem dados aninhados e relacionais | Recursos mapeiam limpo para endpoints |
| Você agrega várias fontes de backend | Um serviço é dono dos dados |
| Banda é preciosa (mobile-first) | Cache HTTP/CDN carrega a carga de leitura |
| Uma plataforma gera o schema para você | O time teria que construir e blindar tudo à mão |
Limitações e trade-offs
- A flexibilidade é paga pelo servidor. Queries arbitrárias do cliente exigem que o servidor seja seguro sob qualquer formato — batching, limites de custo e guardas de profundidade são pré-requisitos, não acabamento.
- Cache vira projeto seu. O que o HTTP dava de graça ao REST, times de GraphQL reimplementam em caches de cliente e persisted queries.
- Observabilidade precisa ser reaprendida. Um endpoint só, respostas sempre-200 e timing por campo exigem ferramental que entenda GraphQL.
- Upload de arquivos e dados binários são desajeitados — em geral delegados a endpoints de upload separados, ao lado do grafo.
- Governança de schema é organizacional. Um contrato compartilhado entre times precisa de regras de ownership; federação (compor subgraphs de cada time em um supergraph) é a resposta de escala — e uma disciplina própria.
GraphQL no Back4app
O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. A parte distintiva é de onde vem o schema: defina um modelo de dados e a plataforma gera a API GraphQL — tipos de objeto tipados, campos de query e mutation, connections que atravessam relações como o exemplo posts → author acima — sem resolvers para escrever, já que o Back4app os implementa contra o seu banco com permissões aplicadas a cada requisição. As abas de código mostram a história inteira do lado do cliente: um POST para /graphql com as chaves do seu app. Um console GraphQL embutido cobre a exploração, o REST continua disponível sobre os mesmos dados para leituras amigáveis a cache, e a lógica customizada entra no schema como funções de Cloud Code — a seção de custos honestos acima vira, em grande parte, conta da plataforma, não sua.
Perguntas frequentes
O que é GraphQL em termos simples?
Uma linguagem de consulta que permite ao cliente pedir a uma API exatamente os campos de que precisa — relações aninhadas incluídas — em uma única requisição, mais um runtime de servidor que atende esses pedidos a partir das suas fontes de dados existentes. Ele fica na frente de qualquer banco de dados ou serviço; não é um banco de dados em si.
GraphQL é melhor que REST?
Nenhum dos dois é universalmente melhor. GraphQL vence com clientes diversos, restrições de banda e dados agregados de várias fontes; REST vence em cache HTTP, simplicidade e maturidade de ferramental para CRUD no formato de recursos. O padrão dominante em produção é pragmático: uma camada GraphQL para os frontends sobre internals REST ou RPC.
GraphQL é um banco de dados ou algo como SQL?
Não — é uma linguagem de API na camada de aplicação, agnóstica de armazenamento por design: resolvers podem ler de qualquer banco de dados, de outra API ou de um arquivo. E, apesar do nome, não é uma linguagem genérica de consulta a grafos como SPARQL; você percorre o grafo apenas pelos caminhos que o schema expõe.
O que são queries, mutations e subscriptions?
Os três tipos de operação. Queries leem dados; mutations escrevem — e retornam o novo estado no mesmo round trip, então o cliente se atualiza sem uma busca adicional; subscriptions empurram atualizações em tempo real por uma conexão persistente, tipicamente WebSockets. As três são validadas contra o mesmo schema.
O que é um schema GraphQL?
O contrato tipado entre cliente e servidor, escrito na Schema Definition Language: os tipos de objeto, seus campos e os pontos de entrada raiz Query, Mutation e Subscription. Toda operação que chega é validada contra ele antes da execução, e o ferramental o introspecta para gerar documentação e clientes tipados.
O que é um resolver?
Uma função server-side que busca o valor de um campo — de um banco de dados, de outra API, de qualquer lugar. O runtime percorre cada query e chama o resolver de cada campo solicitado, o que é ao mesmo tempo a flexibilidade do GraphQL e a origem do seu problema N+1, quando os resolvers de itens de lista consultam um a um.
GraphQL só funciona sobre HTTP POST?
Pela especificação, GraphQL é agnóstico de transporte; na prática, é servido em um único endpoint HTTP — por convenção /graphql — geralmente via POST com corpo JSON, com GET permitido para queries e WebSockets carregando as subscriptions. Uma especificação de GraphQL sobre HTTP agora padroniza essas convenções.
Quando NÃO usar GraphQL?
CRUD simples de recursos com clientes uniformes, tráfego de leitura que o cache HTTP e de CDN absorveria, transferência pesada de arquivos e times pequenos sem apetite para batching de resolvers, limitação por custo de query e governança de schema. Nesses casos, uma API REST bem projetada é menos maquinaria para o mesmo resultado.