REST API vs GraphQL: Qual escolher? 

Imagem de perfil de Fernanda Kipper

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.

O que é REST? 

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:

  • Endpoints bem definidos (/users/{id} retorna um usuário específico). 
  • Métodos HTTP semânticos (GET, POST, PUT, DELETE). 
  • Stateless: cada requisição é independente, o servidor não armazena o estado do usuário e todo esse contexto é enviado a cada requisição, por exemplo: 

Exemplo de REST API em Java (Spring Boot)

@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.

Desvantagens do REST ‼️

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.

Overfetching 

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.

Underfetching 

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.

Versionamento de API 

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.

O que é GraphQL? 

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.

Exemplo de GraphQL API em Java (Spring Boot) 

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.

Lidando com objetos aninhados 🚨

Quando um tipo tem relacionamentos com outros (por exemplo: User-> Post), isso pode gerar problemas de performance se:

  1. O relacionamento for carregado sempre, mesmo que o cliente não peça. Lembre que por padrão o resolver vai carregar o objeto completo.
  2. O relacionamento for uma coleção grande (ex: milhares de posts).
  3. A gente cair em N+1 queries — exemplo: buscar 1 usuário + depois buscar os posts de cada usuário individualmente em um loop.

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
    }
  }
}

Vantagens do GraphQL 

✔️ 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… 

GraphQL tem um problema bem conhecido: performance.

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.

REST vs GraphQL no carregamento de tela 

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.

Como resolver? Backend for Frontend (BFF) 

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.

Quando usar cada um? 

REST ainda é uma ótima escolha quando:

  • A API é simples e previsível.
  • Você quer aproveitar cache HTTP para melhorar a performance.
  • Não há mudanças frequentes na estrutura dos dados.

📌 Exemplo: APIs de CRUD simples.

GraphQL brilha quando:

  • O frontend precisa de flexibilidade para buscar dados.
  • O sistema tem múltiplas fontes de dados que precisam ser consolidadas.
  • A API precisa evoluir sem quebrar compatibilidade com clientes antigos.

📌 Exemplo: Feed de notícias do Facebook, onde cada usuário pode ter um feed personalizado (com componentes visuais diferentes mesmo).

Conclusão 

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!