Introdução a GraphQL + API
Em 2012 surgiu uma necessidade, o Facebook precisava aumentar a agilidade de consultas feitas pelo seu aplicativo mobile, pois os dados retornados (nome do usuário, foto, link para visita do perfil, etc…) eram referentes a visualização pelo navegador, e boa parte dessas informações não eram necessárias durante o uso mobile , então seu time de desenvolvedores criou o GraphQL, que veio a se tornar código aberto em 2015.
O que é GraphQL?
GraphQL não é um ORM, nem um framework, e muito menos uma linguagem de programação, ele é uma query language e sua principal função é retornar exatamente o que o usuário solicita, sendo uma das formas de comunicação entre sistemas (API REST, gRPC…), e além disso tornar as APIs mais rápidas, flexíveis e intuitivas.
Como Funciona?
Como se trata de uma tecnologia estaticamente tipada, tudo nela precisa de um tipo associado, e os tipos associados formam um Schema (coleções de objetos dentro de um banco de dados), por conta disso ela tem um formato muito parecido com o JSON, tornando seu uso mais fácil e intuitivo, e vale lembrar que o GraphQL serve para definir dados para uma API e não está relacionado a nenhum banco de dados.
Tipos
Existem tipos básicos que você pode utilizar para formar os seus próprios tipos, e esses são os Scalar Types, sendo eles:
- Int: Número inteiro.
- Float: Número de ponto flutuante.
- String: Uma sequencia de caracteres UTF-8.
- Boolean: Verdadeiro ou Falso .
- ID: Identificador único.
e com eles você modela seus dados usando schemas, por exemplo, para definir um dado que representa um curso com id, name e categoria, precisaríamos do seguinte schema:
type Course {
id: ID
name: String
category: String
}
Query
Os dados de um servidor GraphQL são consumidos através de queries feitas pelo cliente, ou seja, são os métodos que usamos para realizar as nossas consultas, tendo isso em vista, para consumirmos os dados de um curso, podemos escrever algumas queries:
type Query {
// Obs: a ! afirma que a informação retornada sempre será daquele tipo
// retornar um array de cursos
courses: [Course!]!
courseById(id: String): Course!
}
Mutation
Assim como query é usada para bucar dados, mutation é usada para criar, alterar ou deletar dados do banco de dados, ou seja, são os métodos que usamos para realizar a inserção e as alterações dos dados, para realizar a inserção de dados, podemos escrever a seguinte mutation:
type Mutation {
createCourse(name: String!): Course!
}
API REST
GraphQL está crescendo com o passar dos anos e aumentando sua comunidade, ganhando cada vez mais espaço no mercado e se tornando um forte concorrente ao REST.
Enquanto em uma API REST precisamos de diferentes rotas, com seus respectivos métodos HTTP, para termos um CRUD (create, read, update e delete), em GraphQL temos apenas uma rota, e todas as requisições são feitas a partir das queries e mutations. Além disso precisamos ter outras preocupações em REST (status code, versionamento), para atingirmos os requisitos RESTful, enquanto em GraphQL não temos a necessidade de status code, já que as mensagens de sucesso e de erro vem anexadas no corpo da resposta e versionamento também se torna desnecessário já que conseguimos adicionar novos campos e tipos sem alterar as queries que já existem.
Vantagens
- Over-Fetching e Under-Fetching: Esses são os principais problemas que o GraphQL resolve, retornando apenas os dados necessários à consulta. Permitindo assim buscar apenas informações específicas à pesquisa.
- Redução de custos: Reduz custos de transferência de dados, tanto no lado do servidor (transferência) quanto no lado do cliente (banda 4G).
- Velocidade: Tempos de carregamento mais rápidos para seu aplicativo móvel.
Desvantagens
- Cache: Armazenar dados em cache é mais difícil com GraphQL.
- Status Code: As consultas sempre retornam o código de status 200.
Construção API Node + GraphQL
Nesta seção irei documentar a construção de uma API simples em Node utilizando o GraphQL, para colocarmos a teoria em prática e vermos essa tecnologia em funcionamento.
OBS: O artigo não aborda a maioria das tecnologias utilizadas
Passo 1: Iniciando o projeto e instalando dependências
// iniciando package.json
npm init -y
// instalando dependências
npm install typescript ts-node-dev -D
npm install graphql apollo-server
// iniciando tsconfig.json
npx tsc init
// Crie um arquivo chamado server.ts na raiz da aplicação
// adicionando script no package.json para iniciar a aplicação
"scripts": {
"dev": "tsnd --respawn --transpile-only server.ts"
},
Passo 2: Criando o servidor + Hello World
import { ApolloServer, gql } from "apollo-server";
/*
Criando as typeDefs para o nosso Apollo Server:
Aqui iremos adicionar nossas Querys e Mutations
*/
const typeDefs = gql`
type Query {
helloWorld: String!
}
`;
/*
Criando nosso servidor, passando as typeDefs que criamos e os resolvers.
Resolvers: Aqui você vai fazer a implementação das querys e mutations
*/
const server = new ApolloServer({
typeDefs,
resolvers: {
Query: {
helloWorld: () => {
return "Hello World";
},
},
},
});
/*
Acesse o link disponibilizado no seu console e tenha acesso ao Studio.
Lá você ira conseguir testar suas querys e mutations
*/
server.listen().then(({ url }) => {
console.log(`Server running on ${url}`);
});
Passo 3: Criando a API
import { ApolloServer, gql } from "apollo-server";
import { randomUUID } from "node:crypto";
const typeDefs = gql`
//Criando a tipagem de categoria para o GraphQl
type Category {
name: String!
}
//Criando a tipagem de curso para o GraphQl
type Course {
id: String
name: String!
category: [Category!]
}
// criando a query para realizar a busca de cursos
type Query {
courses: [Course!]!
courseById(id: String): Course!
}
// criando a mutation para realizar o cadastro de um curso
type Mutation {
createCourse(name: String!): Course!
}
`;
/*
cirando as interfaces que iremos usar para tipar os cursos que vamos salvar,
neste caso iremos salvar tudo em um array, mas você pode usar um banco de dados
se preferir.
*/
interface Category {
name: string;
}
interface Course {
id: string;
name: string;
category: Category[];
}
const courses: Course[] = [];
// criação do servidor e implementação das querys e mutations
const server = new ApolloServer({
typeDefs,
resolvers: {
Query: {
courses: () => {
return courses;
},
// recebemos como parâmetros parent, args e context
courseById: (_, args) => {
const result = courses.filter((course) => course.id === args.id);
return result[0];
},
},
Mutation: {
createCourse: (_, args) => {
const course = {
id: randomUUID(),
name: args.name,
category: [{ name: "A" }, { name: "B" }],
};
courses.push(course);
return course;
},
},
},
});
// com o servidor rodando, podemos testar nossas querys e mutations pelo studio
server.listen().then(({ url }) => {
console.log(`Server running on ${url}`);
});
Links para se aprofundar
Vídeos
GraphQL // Dicionário do Programador
Fundamentos do GraphQL na prática (Node.js + React) | Decode #019
GraphQL: O mínimo que um Full Cycle Developer precisa saber. feat: Rodrigo Branas
Artigos
GraphQL vs. REST: Qual o Melhor Para o Desenvolvimento de API?
Considerações finais
Já faz um tempo que eu ouço falar de GraphQl com cada vez mais frequência, então resolvi tirar um tempo para estudar, e escrevi este artigo com o intuito de fixar o que eu aprendi, e quem sabe auxiliar ao menos um pouco quem esteja no processo de aprendizado :)
Feedbacks
Estou aberto a feedbacks, correções de informações errôneas e a bater um papo:
Meu Linkedin
Um abraço!