Tech Writers

GraphQL em aplicações dotNET

9 minutos

Nesse artigo falarei sobre GraphQL com enfoque em aplicações dotNet. Aqui, mostrarei como os problemas inerentes de REST motivaram a criação do GraphQL. Em seguida, apresentarei os conceitos básicos da especificação desta linguagem. Depois, apresentarei a biblioteca Hot Chocolate, que é uma das muitas bibliotecas que implementam a especificação do GraphQL. Finalmente, mostrarei um pequeno exemplo de uso da referida biblioteca em uma aplicação dotNet. 

REST

Antes de falarmos sobre GraphQL é necessário falar sobre REST. O termo foi cunhado por Roy Thomas Fielding (2000) em sua tese de doutorado. Neste trabalho, Fielding apresenta REST como um padrão arquitetural para aplicações web definido por cinco restrições: 

  • Client-server: Essa restrição define que a interface com o usuário deve estar separada dos componentes do sistema que processam e armazenam os dados. 
  • Stateless: Essa restrição diz que o cliente não precisa estar ciente do estado do servidor, bem como o servidor não precisa ter ciência do estado do cliente. 
  • Cache: Essa restrição indica que, quando possível, a aplicação servidora deve indicar à aplicação cliente que os dados podem ser armazenados em cache. 
  • Layered system: Essa restrição indica que a aplicação deve ser construída pelo empilhamento de camadas que adicionam funcionalidades umas às outras. 
  • Uniform interface: Essa restrição indica que os recursos da aplicação devem ser disponibilizados de maneira uniforme, de modo que, ao aprender a acessar um recurso, automaticamente sabe-se como acessar os demais. Segundo o trabalho de Fielding, essa é uma das características centrais que distinguem REST dos demais padrões arquiteturais. No entanto, o próprio autor afirma que isso degrada a eficiência da aplicação, pois não há a disponibilização dos recursos de uma maneira que atenda as necessidades específicas de uma determinada aplicação. 

Como é o REST na prática

Na Figura 1 pode-se ver parte da API do OneDrive da Microsoft. Nessa imagem percebe-se a uniformidade no acesso aos recursos. Isso é perceptível quando notamos que, para obter dados, basta enviar uma requisição GET para um endpoint que começa com o termo drive e é seguido pelo nome do recurso e o seu ID. A mesma lógica vale para a criação de recursos (POST), a modificação de recursos (PUT) e a remoção deles (DELETE). 

Acessando a documentação do Google Drive, podemos ver o típico retorno de uma API REST. Percebe-se, na referida documentação, o grande volume de dados que uma única requisição REST pode trazer. Apesar de grande, uma aplicação cliente ainda pode precisar fazer requisições extras para obter mais dados sobre o dono de um arquivo, por exemplo. 

Considerando-se as restrições determinadas por Fielding e os exemplos mostrados, é fácil perceber dois problemas inerentes à REST. O primeiro deles é o tráfego de dados que o consumidor não precisa e o segundo é a eventual necessidade de fazer várias requisições para se obter os dados necessários para se montar uma página web. 

Figura 1 - Acesse o artigo completo aqui.

Entendendo o GraphQL

O GraphQL surgiu em 2012 no Facebook como uma solução para os problemas encontrados no padrão REST. Em 2015, a linguagem tornou-se open source e em 2018 criou-se a GraphQL Foundation que passou a ser responsável pela especificação da tecnologia. 

É importante destacar que GraphQL não é uma biblioteca ou ferramenta. Assim como SQL, GraphQL é uma linguagem para busca e manipulação de dados. Enquanto usamos o SQL em banco de dados, utiliza-se o GraphQL em APIs.

O quadro 1 mostra uma expressão em SQL para recuperar o número de um pedido e o nome do cliente de um banco de dados. Analogamente, o quadro 2 mostra uma expressão GraphQL para obter os mesmos dados de uma API que suporta GraphQL. Nos exemplos, podemos perceber duas grandes vantagens de GraphQL sobre REST. A primeira está presente no fato de GraphQL permitir que o consumidor busque apenas os dados de que necessita para montar sua página web. A segunda está presente no fato de que o consumidor poderia buscar os dados do pedido e do cliente em uma única chamada. 

Quadro 1: Exemplo de um select em um banco de dados relacional. 
Quadro 2: Exemplo de uma expressão GraphQL.

Considero interessante mencionar mais duas características de uma API GraphQL. A primeira delas é a existência de um único endpoint. Ao contrário de REST, em que cria-se um endpoint para cada recurso, em uma API GraphQL todas as queries e mutations são enviadas para o mesmo endpoint.  

A segunda é o fato de uma API GraphQL suportar apenas o verbo POST. Essa é mais uma diferença com relação a uma REST, onde deve-se usar verbos HTTP diferentes em função da intenção da requisição. Portanto, enquanto em uma API REST devemos usar os verbos GET, POST, PUT e DELETE, em uma API GraphSQL usaremos o verbo POST para obter, criar, alterar e remover dados.  

Schema Definition Language

Vamos, agora, falar um pouco sobre a SDL (Schema Definition Language). Ao usar-se um banco de dados relacional é necessário, primeiramente, definir-se o esquema do banco, ou seja, é necessário definir as tabelas, colunas e relacionamentos. Com GraphQL ocorre algo semelhante, ou seja, a API precisa definir um esquema para que os consumidores possam fazer buscas dos dados. Para criar este esquema, usa-se a SDL.  

O site oficial do GraphQL tem uma seção dedicada à SDL. Na referida seção pode-se encontrar uma descrição completa da linguagem para criação de esquemas GraphQL. Nesse texto, apresentarei a sintaxe básica para a criação de um esquema GraphQL. Na Figura 2 pode-se ver parte de um esquema GraphQL criado a partir do uso do Apollo. Podemos ver que o esquema inicia-se com a definição de dois tipos fundamentais: Query e Mutation. No primeiro tipo definimos todas queries que nossa API possuirá. No nosso exemplo, os consumidores poderão buscar por customers, products e orders. O tipo Mutation define quais operações de manipulação de dados estarão disponíveis para o consumidor. No exemplo apresentado, o consumidor poderá criar, alterar e remover customers e products. Porém, quando se trata de pedidos (orders), ele poderá criar, adicionar um item, cancelar e fechar o pedido. 

Além dos tipos Query e Mutation pode-se perceber a presença dos tipos Customer e Product. Em ambos, existem propriedades do tipo ID, String e Float. Esses três tipos, juntos com os tipos Int e Boolean, são chamados de tipos escalares. O esquema também mostra a definição de um enumerado chamado OrderStatus. Já a Figura 3 mostra a definição de tipos Input que são usados para fornecer dados de entrada para queries e mutations. 

Acho importante salientar que a forma de criar-se o esquema varia de acordo com a biblioteca que você escolher. Ao usar-se a biblioteca Apollo para javascript, a definição do esquema pode ser feita através de uma string passada como parâmetro para a função gql ou através da criação de um arquivo (geralmente chamado de schema.graphql). Porém, ao usar-se bibliotecas como Hot Chocolate para dotNet, a definição do esquema é feita pela criação de classes e da configuração de serviços na aplicação. Portanto, a forma de criação do esquema GraphQL pode variar bastante em função da linguagem e da biblioteca escolhida.  

Figura 2.
Figura 3.

Elementos básicos da linguagem GraphQL

Como mencionado anteriormente, GraphQL é uma linguagem e, portanto, possui uma sintaxe. Você pode encontrar o guia completo com sintaxe da linguagem no site oficial do GraphQL. No entanto, neste artigo, descreverei os elementos básicos dela.  

Realiza-se a busca de dados através de queries, que devem iniciar com a palavra-chave query seguida do nome da query. Se ela possui parâmetros, deve-se abrir parênteses e, dentro deles, deve-se colocar o nome de cada parâmetro seguido do seu valor. Os dois pontos (:) devem ser usados para separar o nome do parâmetro do seu valor. Tendo-se finalizado a lista de parâmetros, deve-se fechar os parênteses. Em seguida, deve-se abrir chaves ({) e colocar dentro delas o nome dos campos que se deseja. Com a lista de campos finalizada, deve-se fechar a chave  (}). O quadro 3 mostra um exemplo simples de query. 

Quadro 3: Exemplo de query.

Existem cenários onde os parâmetros da query podem ser complexos. Quando um parâmetro é complexo, ou seja, é um objeto com um ou mais campos, deve-se abrir chaves logo depois dos dois pontos. Dentro das chaves, deve colocar o valor de cada campo do objeto e o seu respectivo valor, sendo que ambos devem ser separados por dois pontos (ver quadro 4). 

Também existem cenários onde os campos da query podem ser complexos. Nesses casos, deve-se abrir chaves logo depois do nome do campo. Dentro das chaves, deve-se colocar os nomes do campo do objeto (ver quadro 5). 

Quadro 4: Exemplo de query.
Quadro 5: Exemplo de query.

As regras descritas até aqui também servem para mutations. No entanto, essas devem ser iniciadas com a palavra chave mutation em vez de query. É interessante notar que existem outros elementos na sintaxe do GraphQL, mas os elementos descritos até aqui são suficientes para a execução da maior parte das queries e mutations. 

Sendo uma linguagem, GraphQL precisa ser implementado por alguma aplicação ou biblioteca. Para que nossa API suporte queries e mutations, precisamos, geralmente, de uma biblioteca. Evidentemente que poderíamos implementar a especificação da linguagem por conta própria, mas isso seria muito improdutivo. A seção “Code” do site GraphQL.org mostra uma lista de bibliotecas que implementam GraphQL para as mais variadas linguagens. Para o mundo dotNet, por exemplo, existem as bibliotecas “GraphQL for .NET”, “Hot Chocolate” e outras. 

Quando fala-se sobre as implementações de GraphQL, é necessário falar-se sobre o conceito de “resolvers”. Um resolver é uma função que é acionada pela biblioteca que implementa GraphQL. Essa função é a responsável por efetivamente buscar os dados solicitados pela query. O mesmo ocorre com as mutations, ou seja, quando a biblioteca recebe a solicitação de execução de uma mutation, a biblioteca identifica o resolver que executará as mudanças no banco de dados (insert, update e delete). Perceba, então, que na maioria das bibliotecas a execução das buscas e das alterações nos dados é feita pelo seu próprio código. Percebe-se, então, que as bibliotecas que implementam GraphQL encarregam-se por interpretar a query/mutation enviada pelo chamador e descobrir qual a função adequada para resolver a query/mutation solicitada. Para ver um exemplo de uma API simples que usa Hot Chocolate, acesse o meu GitHub

Resumindo tudo, GraphQL é uma linguagem criada pelo Facebook com o objetivo de superar os problemas inerentes a REST. A linguagem fornece uma sintaxe simples para se obter dados de uma API bem como alterar dados da mesma. Ela é implementada por uma grande variedade de bibliotecas para as mais diversas linguagens, permitindo que o desenvolvedor crie uma API GraphQL usando a sua linguagem predileta.

Referências

“GraphQL.” Wikipedia, 9 June 2022, en.wikipedia.org/wiki/GraphQL. Acessado em 6 Nov. 2023. 

The GraphQL Foundation. “GraphQL: A Query Language for APIs.” Graphql.org, 2012, graphql.org/. 

Thomas Fielding, Roy. “Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST).” Ics.uci.edu, 2000, ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm. Acessado em 6 Nov. 2023. 

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *