Fernanda KipperAutor
Se você já precisou construir ou consumir uma API, provavelmente se deparou com a escolha entre REST e GraphQL. E aí bate aquela dúvida:
“Será que GraphQL é realmente tudo isso?” “REST ainda é a melhor escolha para a maioria dos casos?”
Spoiler: depende. Como toda resposta de Senior, não existe resposta certa para todos os cenários ahahahahah
Neste post, vou te explicar as diferenças práticas entre os dois, com exemplos em Java, e levantar algumas provocações você pode cair ao adotar GraphQL sem planejamento.
Se você já consumiu qualquer API pública, tem 99% de chance de ter usado REST. REST (Representational State Transfer) é um estilo arquitetural baseado em recursos, seguindo princípios como:
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
}
Chamamos esse endpoint com um simples GET /users/1, e ele retorna um JSON com os dados do usuário.
Apesar de REST ser extremamente consolidado, ele tem alguns problemas que podem impactar a performance e a flexibilidade da nossa API. E conhecer exatamente quais são essas desvantagens vão te permitir tomar a melhor decisão na hora de escolher a abordagem pra desenvolver seu projeto.
Quando a API retorna mais dados do que o necessário.
Imagina uma requisição que retorna 30 campos no response, mas seu frontend só precisava de dois. O resto dos dados nem são utilizados, consumindo banda e processamento à toa.
Quando a API não retorna todas as informações necessárias.
Digamos que o frontend precise de Name, Age e Total Balance. Mas Name e Age vêm do endpoint /user/info, enquanto Total Balance está em /user/balance.
Isso significa que o frontend precisa fazer duas chamadas para montar uma única tela. Se você já sofreu com múltiplas requisições encadeadas, sabe o quanto isso pode ser um pesadelo.
Em REST, quando a estrutura dos dados muda, a solução clássica é criar novos endpoints, tipo:
/users/v1 /users/v2
O problema? Você precisa manter múltiplos endpoints ativos e garantir compatibilidade entre versões. Ou seja, mais trabalho para o time de backend.
Agora entra em cena o GraphQL, que muda completamente o jogo. Em vez de múltiplos endpoints fixos, ele permite consultas dinâmicas, onde o cliente decide exatamente quais dados quer receber.
Foi criado pelo Facebook para otimizar o feed de notícias, e desde então foi adotado por empresas como GitHub, Netflix e Shopify.
O schema é como o contrato da API: ele define o que pode ser pedido (queries e mutations), quais dados estão disponíveis (tipos), e como a estrutura da resposta será.
type Query {
getUser(id: ID!): User
}
type User {
id: ID
name: String
email: String
}
Já o nosso resolver executa o que foi pedido na query, usando a lógica que você escreveu para recuperar esses dados na sua base. Nesse caso, ele usa meu UserService para pegar os dados do usuário, por padrão, o resolver retorna o objeto completo, e o GraphQL “filtra”os campos conforme o que foi pedido na query.
@Component
public class UserResolver implements GraphQLQueryResolver {
private final UserService userService;
public UserResolver(UserService userService) {
this.userService = userService;
}
public User getUser(Long id) {
return userService.getUserById(id);
}
}
Agora, podemos consultar os dados de um usuário usando uma query, mais ou menos assim:
query {
getUser(id: 1) {
name
}
}
Com uma única chamada, pegamos exatamente os dados necessários. Sem desperdício.
Quando um tipo tem relacionamentos com outros (por exemplo: User-> Post), isso pode gerar problemas de performance se:
Para resolver isso, vamos criar um resolver separado e o GraphQL vai chamá-los somente se o cliente pedir.
type Query {
getUser(id: ID!): User
}
type User {
id: ID
name: String
email: String
posts: [Post]
}
type Post {
id: ID
title: String
content: String
}
@Component
public class UserResolver implements GraphQLQueryResolver {
private final UserService userService;
public UserResolver(UserService userService) {
this.userService = userService;
}
public User getUser(Long id) {
return userService.getUserById(id); // Aqui NÃO carregamos posts!
}
}
@Component
public class UserFieldResolver implements GraphQLResolver {
private final PostService postService;
public UserFieldResolver(PostService postService) {
this.postService = postService;
}
public List getPosts(User user) {
// Só busca se a query pedir "posts"
return postService.getPostsByUserId(user.getId());
}
}
E então poderíamos realizar uma query já buscando pelos dados do usuário e todos posts que ele criou, sem gerar problemas de performance. Por exemplo com a query a seguir:
query {
getUser(id: 1) {
name
posts {
title
}
}
}
✔️ Sem overfetching nem underfetching (o cliente pede só o que precisa).
✔️ Menos chamadas à API, porque os dados vêm completos em uma única request.
✔️ Facilidade para evolução: podemos adicionar novos campos sem quebrar a compatibilidade.
Isso resolve um problema clássico do REST: em GraphQL, novos campos podem ser adicionados sem impactar clientes antigos, pois quem consome a API escolhe quais informações quer receber.
Mas nem tudo são flores…
O que acontece?
Quando fazemos uma query GraphQL, podemos estar puxando dados de múltiplas tabelas no banco.
Na superfície, parece ótimo: uma única requisição resolve tudo. Mas no backend, isso pode significar várias queries encadeadas, aumentando o tempo de resposta.
No REST, os endpoints são modularizados, então podemos carregar diferentes partes da tela de forma assíncrona.
Já no GraphQL, o frontend espera tudo ficar pronto antes de renderizar. Se a query for pesada, pode travar o carregamento da tela inteira.
Uma solução comum para evitar essas dores é usar GraphQL como um BFF (Backend for Frontend). O BFF é uma camada intermediária entre o frontend e os microsserviços. Ele permite:
✔️ Controlar quais queries GraphQL estão disponíveis para cada frontend.
✔️ Evitar sobrecarga de banco, pois podemos usar cache e otimizações.
✔️ Fazer pré-processamento dos dados, reduzindo carga no client.
📌 Exemplo: APIs de CRUD simples.
📌 Exemplo: Feed de notícias do Facebook, onde cada usuário pode ter um feed personalizado (com componentes visuais diferentes mesmo).
Na minha visão, se você precisa de algo simples e confiável, REST ainda é uma escolha sólida.
Se busca mais flexibilidade, GraphQL pode ser a melhor aposta, desde que você implemente bem.
Agora quero saber: qual dessas soluções você tem usado mais no seu dia a dia? Tem alguma experiência para compartilhar?
Comenta aqui no vídeo e bora trocar uma ideia!