Se você já trabalhou com APIs REST por um tempo, provavelmente já se deparou com alguns problemas clássicos: over-fetching (buscar dados desnecessários), under-fetching (múltiplas requisições para obter dados relacionados) e a constante evolução de endpoints conforme as necessidades dos clientes mudam. É aqui que o GraphQL entra em cena, oferecendo uma alternativa elegante e flexível.
O que é GraphQL?
GraphQL não é apenas mais uma tecnologia da moda - é uma linguagem de consulta e um runtime para APIs que resolve problemas reais do desenvolvimento moderno. Criado pelo Facebook em 2012 e tornado público em 2015, o GraphQL permite que os clientes solicitem exatamente os dados que precisam, nem mais nem menos.
A principal diferença em relação ao REST é a flexibilidade: em vez de ter múltiplos endpoints fixos, você tem um único endpoint que pode retornar diferentes estruturas de dados baseadas na consulta (query) enviada pelo cliente.
Vantagens do GraphQL
Flexibilidade nas consultas: O cliente define exatamente quais campos quer receber, evitando over-fetching e reduzindo o tráfego de rede.
Evolução da API sem versionamento: Novos campos podem ser adicionados sem quebrar clientes existentes, e campos deprecados podem ser mantidos por compatibilidade.
Tipagem forte: O schema do GraphQL define tipos claros, facilitando a validação e a documentação automática.
Resolução otimizada: Campos são resolvidos sob demanda, permitindo otimizações como lazy loading.
Implementando GraphQL com Spring Boot
Vamos criar um exemplo prático de uma API que gerencia empréstimos e suas parcelas. Nosso foco será demonstrar como campos específicos podem executar lógicas adicionais apenas quando solicitados.
Configuração Inicial
Adicione a dependência do GraphQL no seu projeto:
Maven (pom.xml
):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
Gradle (build.gradle
):
implementation 'org.springframework.boot:spring-boot-starter-graphql'
Habilite o GraphiQL no application.properties
:
spring.graphql.graphiql.enabled=true
spring.graphql.graphiql.path=/graphiql
Definindo o Schema GraphQL
Crie o arquivo schema.graphqls
em src/main/resources/graphql/
:
type Query {
emprestimo(id: ID!): Emprestimo
emprestimos: [Emprestimo!]!
}
type Emprestimo {
id: ID!
nome: String!
data: String!
valor: Float!
parcelas: [Parcela!]!
}
type Parcela {
numero: Int!
valor: Float!
pago: Boolean!
}
Records para os Dados
import java.math.BigDecimal;
import java.time.LocalDate;
public record Emprestimo(
Long id,
String nome,
LocalDate data,
BigDecimal valor
) {
}
public record Parcela(
Integer numero,
BigDecimal valor,
Boolean pago
) {
}
Implementando os Resolvers
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping;
import org.springframework.stereotype.Controller;
import java.util.List;
@Controller
public class EmprestimoQueryResolver {
private final EmprestimoService emprestimoService;
private final ParcelasService parcelasService;
public EmprestimoQueryResolver(EmprestimoService emprestimoService, ParcelasService parcelasService) {
this.emprestimoService = emprestimoService;
this.parcelasService = parcelasService;
}
@QueryMapping
public List<Emprestimo> emprestimos() {
return emprestimoService.findAll();
}
@QueryMapping
public Emprestimo emprestimo(@Argument String id) {
return emprestimoService.findById(Long.parseLong(id));
}
@SchemaMapping
public List<Parcela> parcelas(Emprestimo emprestimo) {
// Esta lógica só executa quando o campo 'parcelas' é solicitado na query
System.out.println("Carregando parcelas para: " + emprestimo.nome());
return parcelasService.findByEmprestimoId(emprestimo.id());
}
}
O @SchemaMapping
usa convenções para fazer o vínculo automaticamente: o tipo do parâmetro (Emprestimo
)
identifica o
tipo GraphQL, e o nome do método (parcelas
) mapeia para o campo no schema.
Services para Lógica de Negócio
@Service
public class EmprestimoService {
public List<Emprestimo> findAll() {
return Mocks.EMPRESTIMOS;
}
public Emprestimo findById(Long id) {
return Mocks.EMPRESTIMOS.stream()
.filter(emprestimo -> emprestimo.id().equals(id))
.findFirst()
.orElse(null);
}
}
@Service
public class ParcelasService {
public List<Parcela> findByEmprestimoId(Long emprestimoId) {
return Mocks.getParcelasByEmprestimoId(emprestimoId);
}
}
Mocks
public class Mocks {
public static final List<Emprestimo> EMPRESTIMOS = List.of(
new Emprestimo(1L, "Empréstimo do FGTS", LocalDate.of(2023, 1, 10), new BigDecimal("15000.00")),
new Emprestimo(2L, "Empréstimo Consignado", LocalDate.of(2023, 2, 15), new BigDecimal("20000.00")),
new Emprestimo(3L, "Empréstimo Pessoal", LocalDate.of(2023, 3, 20), new BigDecimal("8000.00"))
);
private static final Map<Long, List<Parcela>> EMPRESTIMO_PARCELAS_MAP = Map.of(
1L, List.of(
new Parcela(1, new BigDecimal("5000.00"), false),
new Parcela(2, new BigDecimal("5000.00"), false),
new Parcela(3, new BigDecimal("5000.00"), false)
),
2L, List.of(
new Parcela(1, new BigDecimal("4000.00"), true),
new Parcela(2, new BigDecimal("4000.00"), false),
new Parcela(3, new BigDecimal("4000.00"), false),
new Parcela(4, new BigDecimal("4000.00"), false),
new Parcela(5, new BigDecimal("4000.00"), false)
),
3L, List.of(
new Parcela(1, new BigDecimal("4000.00"), true),
new Parcela(2, new BigDecimal("4000.00"), false)
)
);
public static List<Parcela> getParcelasByEmprestimoId(Long emprestimoId) {
return EMPRESTIMO_PARCELAS_MAP.get(emprestimoId);
}
}
Testando a API
O Spring Boot disponibiliza um GraphiQL em http://localhost:8080/graphiql para testar suas queries.
Query simples (não carrega as parcelas):
query {
emprestimos {
id
nome
valor
data
}
}
Filtro por ID e com parcelas (executa a lógica adicional):
query {
emprestimo(id: "1") {
id
nome
valor
parcelas {
numero
valor
pago
}
}
}
Observe que apenas na segunda query você verá as mensagens de log indicando que a consulta das parcelas foi executada!
Vantagens Práticas Observadas
No nosso exemplo, fica claro como o GraphQL permite otimizações importantes:
- Lazy Loading Inteligente: As parcelas só são carregadas quando solicitadas, economizando recursos computacionais e consultas ao banco.
- Flexibilidade do Cliente: O frontend pode decidir quando precisa de dados detalhados (parcelas) vs. quando precisa apenas de informações básicas do empréstimo.
- Evolução Gradual: Novos campos podem ser adicionados ao schema sem impactar queries existentes.
Conclusão
O GraphQL com Spring Boot oferece uma combinação poderosa para criar APIs modernas e eficientes. A capacidade de resolver campos sob demanda, como demonstrado no exemplo das parcelas de empréstimo, permite otimizações significativas e maior flexibilidade para os clientes da API.
Para projetos onde a flexibilidade das consultas é importante e onde você quer evitar o over-fetching comum em APIs REST, o GraphQL é definitivamente uma tecnologia que vale a pena considerar. A integração com Spring Boot torna a implementação mais simples e aproveita todo o ecossistema já conhecido pelos desenvolvedores Java.
O código completo deste projeto de exemplo está disponível no meu GitHub.