DEV Community

Cover image for Entendendo um pouco sobre o que é GraphQL
Amaury Gonçalves Costa
Amaury Gonçalves Costa

Posted on • Updated on

Entendendo um pouco sobre o que é GraphQL

E aí galera!!! Tudo bem com vocês?

Então, muito empolgado por estar escrevendo aqui hoje. Este é minha primeira contribuição em forma de artigo para toda a comunidade DEV e agradeço se ao final puderem me dar seu feedback sobre o que acharam, se falei alguma bobagem, o que posso melhorar, etc. Show de bola? Vamos começar então.

Particularmente, GraphQL é um assunto que gosto de conversar muito. Após passar um tempo construindo APIs REST, conhecer o GraphQL foi meio que uma libertação em muitas coisas, rsss. Isto trouxe para nós desenvolvedores uma nova forma de desenvolver APIs e que, para quem não conhece, acredito que irão curtir bastante. Este não será o único artigo que irei tratar de GraphQL. A ideia para este será estudar a teoria. Nos próximos, irei demonstrar de forma prática tanto no frontend (utilizando o Angular) quanto no backend (utilizando a linguagem Golang).

O QUE É GRAPHQL?

GraphQL é uma query language para APIs, ou seja, é uma linguagem de consulta de dados em APIs, que foi desenvolvida pelo Facebook. O facebook utiliza o GraphQL em suas aplicações desde 2012 e somente em junho de 2015 eles liberaram.

É uma alternativa mais eficiente, poderosa e flexível em relação ao REST. As consultas são interpretadas em tempo de execução no servidor usando um sistema de tipos que você define para seus dados.

Ao trabalhar com GraphQL você irá perceber que ele é uma abstração do protocolo HTTP. Mas como assim? Basicamente, na aplicação que estiver subindo o server GraphQL, você estará utilizando um único endpoint. Este server irá receber request do tipo POST e GET (sendo POST o mais utilizado), tendo como resposta um formato JSON. Parece estranho para quem está acostumado com REST, em que temos vários endpoint, mas aguarde um pouco que você irá entender a ideia.

Uma pequena confusão com o Graphql

É muito comum a princípio as pessoas acharem que GraphQL é um banco de dados. Mas definitivamente, NÃO É. Veja bem, o fato de ter Query Language no nome não implica que ele é banco de dados, mas sim que é uma Linguagem de Consulta para APIs. Em resumo, o mesmo não está atrelado a nenhum banco de dados ou qualquer sistema de armazenamento, não é ORM. Na verdade, ele nem precisa de banco de dados para funcionar.

Fechando este tópico sobre o que é GraphQL, algumas de suas características seriam:

  • Fornece a liberdade ao cliente de especificamente quais dados irá precisar.
  • Trabalha com um sistema de tipos, sendo este, responsável por definir os dados.
  • Fazendo uma analogia, é como se fosse uma camada ali no nosso backend que tem a capacidade de buscar dados de outras fontes (outros bancos de dados, outras APIs por exemplo).

ENTENDENDO OS CONCEITOS BÁSICOS EM TORNO DO GRAPHQL

Type System

Sistema de tipos. Tudo no GraphQL é tipado, sendo que o mesmo tem seu próprio sistema de tipos que nos permite descrever os dados da nossa API. Podemos ver um exemplo disso na Figura 1, onde foi criado um projeto no VSCode e um arquivo chamado "schema.graphql".

Figura 1: Category and Subcategory Type

Alt Text

Fonte: o autor

Observe duas particularidades: a primeira é o sinal de “!” no ID. Isto significa que não é permitido aceitar null como valor. O segundo ponto é o campo “subcategories”. Podemos assumir ali que há um relacionamento entre Category e Subcategory, sendo que Category podem ter N subcategories. Mais detalhes dos tipos veremos um pouco mais adiante.

Schema

E o que contém todos os Types da nossa API. Em síntese, seu papel é definir o esquema da API. Ela engloba nossas Queries, Mutations, Subscriptions, Diretivas, etc. Exemplo:

type Schema {
query: Query
mutation: Mutation
subscription: Subscription
}

Query

Quando você quer realizar uma consulta, uma busca dos dados na API. A Figura 2 mostra a declaração de uma query em nosso arquivo “schema.graphql”. A Figura 3 mostra uma requisição feita pelo Insomnia a nossa API e seu retorno em JSON. Detalhes da implementação da API ficará para outro momento:

Figura 2: Query

Alt Text

Fonte: o autor

Figura 3: Realizando uma query à nossa API

Alt Text

Fonte: o autor

Contextualizando: eu criei uma API GraphQL simples, escrita em Golang. Nesta API, temos a implementação para devolvermos estes dados. Por fim, utilizei o Insomnia para realizar a request a minha API, puxando todas as categorias cadastradas.

Temos algumas observações aqui: em nosso schema, repare que informamos o type Query. Este tipo costuma ser chamado de RootQuery. Poderíamos comparar que o RootQuery será a instância Pai de todas as queries que podemos criar. Neste nosso exemplo, temos apenas findCategories.

Um outro ponto muito importante sobre as queries: os campos são resolvidos paralelamente. Comentarei um pouco disso abaixo.

Mutation

Será o responsável por fazer mudanças dos dados na API. A ideia é que as Mutations nos permite criar, alterar e excluir dados. A Figura 4 mostra a declaração de uma Mutation. A Figura 5 mostra uma mutation sendo requisitada a nossa API pelo Insomnia:

Figura 4: Mutation

Alt Text

Fonte: o autor

Figura 5: Realizando uma mutation à nossa API

Alt Text

Fonte: o autor

De maneira análoga a Query, em nosso schema, repare que informamos o type Mutation. Este tipo costuma ser chamado de RootMutation. Poderíamos comparar que o RootMutation será a instância Pai de todas as mutations que podemos criar. Neste nosso exemplo, temos apenas newCategory.

Já nas mutations, diferentemente das queries, os campos são resolvidos em série, um após o outro.

Mas então, dissemos anteriormente que na query os campos são resolvidos paralelamente e as mutations em série. O que isto quer dizer? Pois bem, isto é de extrema relevância pelo fato de que se você quiser escrever dados em sua API utilizando query ao invés de mutation isto é possível. No entanto, assim como muita coisa no desenvolvimento de software, aqui no GraphQL também temos um padrão a seguir. Pelo fato de que mutations são resolvidos em série, há o risco de que se você for utilizar query para realizar alterações na API, isto pode causar um efeito indesejado, pelo fato de que queries são resolvidas paralelamente. Devido a isto, siga sempre este padrão: query para ler dados, mutation para escrever dados.

Subscriptions

Você utiliza subscription quando você precisa trabalhar em Real Time, escutando por mudanças na API. É meio que você precisará de um Web Socket para fazer isto.

Resolvers

Esta parte é muito importante de entender. Repare que apontamos os nossos tipos, declaramos nossas queries e mutations por exemplo. No entanto, apenas com esta declaração sua API não está pronta. Não como se fosse uma coisa mágica, por exemplo, que a query findCategories irá no banco de dados sem que você implemente algo para realizar este comportamento. Para isto temos “Resolver”.

É importante entender que cada campo no GraphQL possui uma função Resolver. Por exemplo, a partir da query findCategories poderia ser gerada uma função resolver para que, se o cliente chamasse esta query, sua API devolveria uma resposta, o json de categorias neste caso. Este comportamento é feito em uma função Resolver.

A Figura 6 mostra um exemplo disso, em que irei abordar mais detalhadamento em um próximo artigo (quando construíremos nossa API GraphQL utilizando Golang):

Figura 6: Função resolver para query “findCategories”

Alt Text

Fonte: o autor

Repare que nesta função você poderia estar buscando as categorias em um banco de dados por exemplo. Apesar de não termos mostrado na Figura 6, uma função resolver pode receber 4 argumentos: parent (ou object), args, context e info.

parent: é como se fosse o “pai” do resolver naquele momento. Por exemplo, na Figura 2, se chamássemos o campo “subcategories” na query, o root para este resolver seria “findCategories”.

args: seriam os argumentos que você enviasse. Se você quisesse, por exemplo, buscar uma categoria pelo UUID, o argumento “args” teria este valor para que você pudesse realizar a consulta.

context: aqui você pode fornecer valores do contexto, como o usuário conectado no momento, uma instância do banco de dados, dentre outras coisas.

info: seriam as informações específicas para a query ou mutation que está sendo invocada, bem como detalhes do schema. Exemplo, você poderia saber aqui quais campos foram requisitados na query naquele momento.

Trivial Resolvers

Aqui também é muito interessante porque será o momento em que você resolverá os campos daquele type. Como assim? Ainda na Figura 2, se precisássemos mostrar todas as subcategorias associado as categorias? Se pensarmos em banco de dados, “subcategories” seria outra tabela associada a “categories”. Portanto, ao invocar este campo na query, você poderia criar uma função resolver que resolverá apenas este campo. Esta função é considerada um Resolver Trivial.

Scalar Types

Até agora, vimos que um objeto GraphQL tem um nome e seus campos. No entanto, estes campos precisam ser resolvidos de maneira concreta. Ou seja, precisamos dos Tipos Escalares. Basicamente, os tipos escalares são:

  • Int: um inteiro de 32 bits (assinado)
  • Float: ponto flutuante de dupla precisão (assinado)
  • String: Sequência de caracteres
  • Boolean: true ou false
  • ID: Representa um identificador único.

É possível também trabalhar com Custom Scalar Types. Um exemplo e o que é bem comum de se ver seria um tipo data. Sintaticamente seria assim:

scalar Date

Após isto, é necessário implementar uma forma serializar ou deserializar este tipo.

CARACTERÍSTICAS DO GRAPHQL E UMA PEQUENA COMPARAÇÃO COM O REST

GraphQL é uma árvore

Observando as figuras anteriores em que trabalhamos com GraphQL, esta maneira em que os campos são resolvidos no GraphQL seria similar a uma estrutura de dados bem conhecida, árvore. Isto pode ser visualizado na Figura 7:

Figura 7: GraphQL is a tree

Alt Text

Fonte: Pandya, 2021

GraphQL é melhor que REST

Há algumas discussões em torno disso e sabemos que tudo na TI depende. No entanto, eu comungo desta opinião. Com o passar dos anos, APIs REST se mostraram inflexíveis em alguns contextos. O GraphQL foi desenvolvido com isto em mente, para lidar com estas necessidades com maior eficiência e flexibilidade. Se você já trabalhou com REST, alguns pontos abaixo você vai entender melhor ainda o que o GraphQL resolve:

No underfetching

Isto se remete ao fato de quando um endpoint não fornece informações suficientes. Já com GraphQL não é necessário implementar vários endpoints para obter os dados necessários. Vamos observar a Figura 8:

Figura 8: Underfeching

Alt Text

Fonte: HowtoGraphQL, 2021

Quando você trabaha com REST, se você precisasse buscar um usuário pelo ID, você criaria um endpoint do tipo: /user/id. Perfeito! Agora se você precisasse dos posts deste usuário. Seguindo as normas REST você faria: -user/id/posts. Entendeu a ideia? Você não tem tudo o que precisa em um só endpoint. Caso você precise de mais detalhes, é necessário ficar criando novas rotas para suprir esta necessidade. Com o GraphQL você não passa por este problema. Um dos motivos, conforme foi dito no início deste artigo, é que a requisição é feita somente a um único endpoint. A partir dele, você requisita o que você precisa. Este exemplo pode ser visualizado na Figura 9:

Figura 9: No underfetching

Alt Text

Fonte: HowtoGraphQL, 2021

No overfetching

Isto diz respeito ao fato de que o cliente faz download de mais dados do que o necessário na request para seu cliente. Em GraphQL? Tchauzinho sobrecarga.
Exemplo: hipoteticamente, você tem uma tela em que lista os dados de um produto. Para esta tela, você precisaria apenas de descrição do produto, valor e quantidade. No REST, você faria um endpoint mais ou menos assim: /products/id. Bacana. Aí em outra tela, você precisa de todos os dados deste produto. O que você faria? Requisitaria a mesma rota? Ok. Mas veja bem, na segunda tela você precisa de todos os dados do produto e faz sentido vocẽ transferir todos estes dados. Na primeira tela não. Não faz sentido sua API mandar para o cliente todos os dados do produto se você somente está precisando de tres campos. Se observarmos a Figura 9, com o GraphQL, você pode ir pedindo apenas o que você precisa. No REST, a solução para isto, ou você continua enviando os dados e filtra apenas o que precisa no front ou você cria 2 rotas, uma para todos os dados outra apenas para os 3 dados. Começa a fica esquisito, correto?

Prototipagem

Uma vez que GraphQL é flexível e trabalha somente com um endpoint, realizar uma prototipagem pelo menos ficou bem escalável. Repare que você não precisaria de ajustes no backend quando a estrutura de dados precisa ser mudada. No REST, é comum você construir endpoints tomando como base as views da aplicação.

Type System

O GraphQL, como vimos, possui um sistema de tipos forte, que são expostos e que servem parecido como um contrato entre cliente e servidor para especificar como os dados podem ser acessados. O legal do esquema é que ele também pode servir como documentação de sua API.

Bibliotecas

Diversas linguagens suportam GraphQL. Na própria documentação é possível ver estas linguagens, desde bibliotecas para frontend quanto backend. Seguem alguns exemplos:

  • Go: graphql-go, 99designs/gqlgen, graphql-relay-go
  • Java: graphql-java
  • JavaScript: GraphQL.js, express-graphql, apollo-server
  • PHP: graphql-php, Siler
  • Python: graphene

CONSIDERAÇÕES FINAIS

Ufa galera!! Chegamos ao fim deste artigo. Acredito que foi bastante conteúdo e pode parecer impossível de aprender, mas não é. Obviamente, utilizar uma nova tecnologia a princípio, leva um tempo para você ir se acostumando. Mas garanto a vocês que GraphQL trouxe uma nova forma de construir APIs de forma espetacular.

Eu espero muito ter contribuído para o enriquecimento do conhecimento de vocês. Como falei durante este artigo, não vou parar por aqui. No próximo, mostrarei de maneira prática
de como podemos construir nossa API GraphQL utilizando a linguagem Golang. Quando a mesma estiver concluída, deixarei como referência aqui para vocês. Muito obrigado por terem lido até aqui e ajudaria muito o feedback de vocês. Até a próxima!!!!

REFERÊNCIAS

Top comments (1)

Collapse
 
souzalfalves profile image
Luiz Fernando Alves Souza

Artigo ficou excelente Amaury, aguardo os proxímo como disse aí... irei implementar meu primeiro código em Golang com GraphQL a partir dos seus posts rsrs... Parabéns.