Você já ouviu falar em linguagem de consulta? O GraphQL recebe essa atribuição pois possibilita o acesso às informações de um banco de dados de forma extremamente flexível. Trata-se de uma tecnologia que ajuda a reduzir o volume de dados trafegados entre as requisições e, como consequência, melhora a performance da aplicação.

Essa tecnologia é relativamente nova, já que foi desenvolvida pelo Facebook em 2012 e disponibilizada como open source em 2015. Para que você entenda um pouco mais sobre essa poderosa ferramenta, fizemos este guia inicial da linguagem. Vamos mostrar:

Vamos lá? Boa leitura!

O que é GraphQL?

GraphQL é uma linguagem de consulta utilizada para a manipulação de dados em APIsApplication Programming Interface — e contém um serviço de runtime, que é utilizado para processar as consultas efetuadas. É importante entender que ele não é uma linguagem de programação, um framework ou um banco de dados.

Na realidade, o GraphQL pode ser utilizado em conjunto com esses recursos, como também pode retornar dados que não estejam armazenados em um banco de dados. Logo, ele pode ser utilizado com qualquer linguagem de programação como PHP, Rubi, Java, entre outras. Uma das formas mais utilizadas é com JavaScript e Node.js.

Na prática, o GraphQL utiliza schemas para definir toda a estrutura de relacionamentos e queries que serão utilizadas para a manipulação de dados no back-end. Esse, por sua vez, utiliza outro recurso da linguagem, os resolvers, para retornar as informações definidas no schema. Falaremos um pouco mais sobre esses conceitos no próximo tópico.

Portanto, a linguagem permite a execução de uma consulta ou mutação na base de dados de uma forma simples e rápida. Em vez de retornar um grande volume de dados em cada requisição, o GraphQL permite a recuperação apenas do que será consumido pela aplicação.

Além disso, ele permite que essa requisição seja feita uma única vez, ou seja, sem a necessidade de executar diversas solicitações ao servidor para recuperar o conteúdo desejado. Por isso é considerado uma linguagem tão flexível, já que reduz a quantidade de requisições ao servidor e de dados trafegados.

O objetivo do GraphQL é facilitar a forma de manipular dados do back-end. Por isso, utiliza um serviço para processar as requisições efetuadas. Existem algumas opções disponíveis para isso, entre elas o servidor Apollo GraphQL, que possibilita a conexão com banco de dados e muitos outros recursos.

Quais os princípios do GraphQL?

O GraphQL utiliza uma série de princípios que fazem parte de sua implementação em uma aplicação. Confira cada um deles a seguir.

Schema

Para utilizar o GraphQL, é preciso compreender que todos os dados movimentados devem ser declarados em types. O schema permite que a aplicação front-end defina quais os formatos de dados ela deseja receber do back-end. Para isso, é preciso especificar todos os tipos que serão utilizados nas queries ou mutations.

Imagine que temos, em uma aplicação, pessoas funcionárias e departamentos relacionados e queremos acessar esse conteúdo. Portanto, o schema para esse exemplo seria algo como:

type Funcionario {
           id: ID
           nome: String!
           cargo: String!
           departamento: Departamento!
}
type Departamento {
           id: ID
           descricao: string!
           funcionarios: [Funcionario]!
}

Perceba que além de declararmos os tipos Funcionário e Departamento, criamos um relacionamento entre eles. No tipo Departamento, especificamos que a propriedade funcionarios terá uma lista de objetos do tipo Funcionário. Quando especificamos um tipo entre colchetes no GraphQL, significa que aquela propriedade armazena uma lista do objeto correspondente.

Observe que na propriedade nome colocamos um ponto de exclamação após a definição do tipo string. Isso significa que esse conteúdo é obrigatório. Já na propriedade id, declaramos o seu tipo como ID. Essa é uma representação do GraphQL para indicar que se trata de um campo identificador e único.

Veja que no tipo Funcionário também definimos o relacionamento entre ele e o Departamento. Perceba que esse conteúdo é obrigatório, ou seja, não podemos ter uma pessoa funcionária sem um departamento associado a ela. O schema, portanto, deve conter todas as declarações sobre o que será recuperado da aplicação back-end.

Além dos tipos necessários para a aplicação, o schema também deve conter alguns tipos essenciais para que o GraphQL funcione de maneira adequada. São a query, os mutations e os resolvers. 

Query 

O GraphQL necessita da declaração de queries. São elas que dizem ao back-end quais dados a aplicação necessita. Vamos continuar o nosso exemplo com a criação de queries para a leitura das pessoas funcionárias existentes na nossa aplicação. Veja o código a seguir.

type Query {
    listaFuncionarios: [Funcionario]!
    getFuncionarioPorEmail(email: String!): Funcionario!
}

Perceba que criamos duas queries, a listaFuncionarios retorna um array de funcionários. A outra query é um pouco diferente, pois ela recebe um parâmetro para a recuperação de um resultado mais específico, ou seja, queremos recuperar o um funcionário com base no e-mail informado.

Basicamente, o que é preciso fazer no schema é semelhante à assinatura de uma interface quando utilizamos programação orientada a objetos. Em outras palavras, é preciso declarar dentro do tipo Query todas as consultas que serão feitas na base de dados. Nesse exemplo temos a lista de pessoas funcionárias e o retorno de uma determinada com a busca pelo seu e-mail.

Mutations 

Como vimos, a query faz uma consulta na base de dados. Já o mutation executa mudanças no conteúdo, o que pode ser uma inclusão, alteração ou a exclusão de um registro. Assim como na query, precisamos fazer a declaração no schema de quais os tipos de mutations a aplicação poderá realizar. Confira o código a seguir.      

type Mutation {
    criarFuncionario(nome:String, cargo: String, email: String, departamento: String): Funcionario
}

Perceba que criamos um tipo chamado Mutation que contém a declaração da função necessária para a inclusão de um novo registro de Funcionário.

Subscriptions

A subscription é uma funcionalidade do GraphQL que permite a exibição de notificações à pessoa usuária da aplicação sempre que um evento predefinido for executado. Desse modo, se um novo registro for criado no banco de dados, por exemplo, é possível exibir a notificação desse evento.

Uma forma de fazer isso é por meio de uma conexão WebSockets, que permite a comunicação bidirecional com o servidor. Assim sendo, é possível obter a notificação de forma praticamente instantânea.

Resolvers

O resolver é outro princípio importante em GraphQL. Ele é responsável pela execução das queries definidas no schema da aplicação. Os dados utilizados em uma aplicação que utiliza GraphQL pode ter origem em um banco de dados ou até mesmo em um array de objetos.

Os resolvers não são declarados como tipos. Em vez disso, são funções que executam as queries ou mutations declaradas no schema. Veja o código a seguir.

const resolvers = {
  Query: {
    listaFuncionarios: () => funcionarios,
    getFuncionarioPorEmail: (_ , args) => {
      return funcionarios.find((funcionario) => funcionario.email === args.email);
    }
  
  },
  Mutation: {
    criarFuncionario(_, args){  
      const novoFuncionario ={
      id: ++funcionarios.length,
      nome: args.nome,
      cargo: args.cargo,
      email: args.email,
      departamento: args.departamento
    };
      funcionarios.push(novoFuncionario);
      return novoFuncionario;
    }
  }
};

Perceba que criamos uma constante chamada resolvers e precisamos definir todas as funções declaradas no schema. Cada função deve ser escrita dentro de seu tipo de origem. As funções referentes às queries e mutations, por exemplo, devem ficar dentro de uma declaração como:

Query: {
// declarar as funções referente às queries.
}
Mutation: {
// declarar as funções referente às mutations.
}

No nosso exemplo, criamos uma query para listar pessoas funcionárias, na qual lemos a variável funcionários declarada anteriormente. A seguir, criamos outra função, chamada getFuncionarioPorEmail, que recebe um parâmetro email e retorna uma pessoa.

Já no mutation, criamos uma função chamada criarFuncionarios para simular a inclusão de um novo registro. Vale ressaltar que, nesse exemplo, simulamos a criação do id apenas para a execução do teste. Além disso, utilizamos uma massa de dados conforme o código a seguir:

var funcionarios = [
  {id: 1, nome: 'José da Silva', cargo: "Analista de Sistemas", email: "[email protected]", departamento:1},
  {id: 2, nome: 'Maria Garcia', cargo: "Analista Financeiro", email: "[email protected]", departamento: 2},
  {id: 3, nome: 'Joana Flores', cargo: "Analista de Sistemas", email: "[email protected]", departamento: 1},
];
const departamentos = [
  {id:1,descricao:"TI"},
  {id:2,descricao:"Financeiro"}
]

Outra funcionalidade do resolver é solucionar problemas que poderiam ocorrer na inclusão de propriedades que representam o relacionamento entre entidades, como o que ocorre com o campo departamento de funcionários, que traz o id correspondente. Podemos criar uma função no resolver para retornar o valor correto. Veja o algoritmo a seguir.

Funcionario: {
    departamento(pessoa) {
      if (isNaN(pessoa.departamento)) 
        return departamentos.find((d) => d.descricao === pessoa.departamento)
      else
        return departamentos.find((d) => d.id === pessoa.departamento)
    },
}

Com essa função, a pessoa usuária da aplicação tanto pode informar o id numérico do departamento quanto o texto correspondente. O resolver fará a busca no objeto Departamento para retornar o valor correto. Confira a depuração da mutation criarFuncionario:

mutation{
 criarFuncionario(
   nome: "Maria Aparecida"
   cargo: "Gerente de Projetos"
   email:"[email protected]"
   departamento: "TI"
 )
 {
   id
   nome 
   cargo
   email
   departamento{
     id
     descricao
   }
 }
}
// resultado
{
 "data": {
   "criarFuncionario": {
     "id": "4",
     "nome": "Maria Aparecida",
     "cargo": "Gerente de Projetos",
     "email": "[email protected]",
     "departamento": {
       "id": "1",
       "descricao": "TI"
     }
   }
 }
}

Quais as vantagens e desvantagens de usar o GraphQL?

Uma das vantagens de utilizar o GraphQL é que ele é fortemente tipado. Isso significa que todos os dados trafegados devem ter um tipo específico declarado. Essa característica deixa a aplicação mais organizada, flexível e independente. Assim, o front-end pode utilizar o que realmente necessita e no formato que for melhor para ele.

Outra vantagem é a facilidade para modificar a aplicação no front-end sem a necessidade de solicitar alterações no back-end. Isso porque os resolvers podem fazer essa tarefa de uma forma extremamente simples em diversas situações, como para aumentar os campos retornados em uma consulta ou adicioná-los em operações de mutations.

A velocidade de resposta e a redução de dados trafegados são outros benefícios da utilização do GraphQL. Isso porque o GraphQL utiliza apenas um endpoint, que é a requisição feita ao servidor.

Por consequência, faz uma única solicitação para acessar o conteúdo desejado. A redução de dados trafegados, por sua vez, ajuda a reduzir o custo de transferência de dados, já que muitos provedores cobram por esse volume.

Mas o GraphQL não é perfeito. É preciso cuidado com a quantidade de relacionamento entre tabelas ao desenvolver uma query. Aninhamentos profundos podem causar lentidão no servidor. Entretanto, esse problema não é exclusivo do GraphQL.

GraphQL vs REST: quais as diferenças?

Antes falar sobre as diferenças entre GraphQL e REST, é importante relembrar o conceito deste. Em essência, REST — Representational State Transfer — é um modelo de arquitetura utilizado para o desenvolvimento de webservices e APIs, e contém uma série de conceitos definidos para a troca de dados por meio do protocolo HTTP.

As requisições REST são utilizadas para realizar consultas e modificações na aplicação e cada uma deve seguir um modelo definido. Por exemplo, para realizar uma consulta utilizamos o método GET do protocolo HTTP. Já para executar uma inclusão no banco de dados, utilizamos o método POST.

O GraphQL e o REST são bem parecidos em algumas questões, já que ambos são utilizados para trafegar informações em uma API e o retorno dos dados geralmente é feito no formato JSON. Entretanto, o REST trabalha com muitos endpoints, as requisições feitas ao servidor. Para recuperar a lista de pessoas funcionárias citada no nosso exemplo anterior, teríamos que executar uma chamada como essa:

http://localhost:3000/funcionario

Já se quiséssemos recuperar uma pessoa específica pelo seu e-mail, devemos utilizar outro endpoint:

http://localhost:3000/funcionario?email=”[email protected]

Os valores retornados por esses endpoints são referentes a todos os campos do objeto solicitado. Imagine que, ao realizar a busca por e-mail, precisássemos somente do atributo nome como retorno. Para obter esse conteúdo com REST seria preciso receber todos os campos e desprezar os que não interessam à aplicação e, assim, utilizar somente a propriedade desejada.

Outra opção seria desenvolver um novo endpoint na aplicação back-end para retornar somente o campo desejado. Entretanto, essa tarefa é mais complicada, pois envolve tempo e custos para o desenvolvimento, além de aumentar o tamanho e a manutenção da aplicação. A esse excesso de dados retornados pelo REST chamamos de over-fetching.

Outra situação é quando um único endpoint não retorna todos os dados necessários para a aplicação e precisamos utilizar mais endpoints para recuperar todos as informações desejadas. Utilizando o exemplo, a propriedade departamento do objeto Funcionário traz o valor numérico de um id, ou seja, não corresponde ao conteúdo que queremos mostrar na aplicação front-end.

Dessa forma, além de recuperar as pessoas, temos que fazer outra solicitação ao servidor para obter cada departamento correspondente ao valor do id para exibir a descrição correta. Nesse caso, quando precisamos executar várias requisições para recuperar o conteúdo, chamamos de under-fetching.

O GraphQL resolve esses dois problemas. No caso do over-fetching podemos criar uma query para retornar apenas o nome referente à busca da pessoa funcionária por e-mail. Confira a query a seguir:

query{
 getFuncionarioPorEmail (email: "[email protected]")  {
      nome
   }
} 
// resultado:
{
 "data": {
   "getFuncionarioPorEmail": {
     "nome": "José da Silva"
   }
 }
}

O problema referente ao under-fetching também pode ser resolvido facilmente, pois é possível executar a solicitação de todos os dados necessários em apenas uma requisição. Confira a query abaixo:

query{
 listaFuncionarios{
   id
   nome
   cargo
   email
   departamento{
     id
     descricao
   }
 }
}
//resultado
{
 "data": {
   "listaFuncionarios": [
     {
       "id": "1",
       "nome": "José da Silva",
       "cargo": "Analista de Sistemas",
       "email": "[email protected]",
       "departamento": {
         "id": "1",
         "descricao": "TI"
       }
     },
     {
       "id": "2",
       "nome": "Maria Garcia",
       "cargo": "Analista Financeiro",
       "email": "[email protected]",
       "departamento": {
         "id": "2",
         "descricao": "Financeiro"
       }
     },
     {
       "id": "3",
       "nome": "Joana Flores",
       "cargo": "Analista de Sistemas",
       "email": "[email protected]",
       "departamento": {
         "id": "1",
         "descricao": "TI"
       }
     }
   ]
 }
}

Em resumo, o GraphQL é uma poderosa linguagem de consulta que surgiu para facilitar a vida das pessoas que desenvolvem softwares. Trata-se de uma ferramenta extremamente flexível, que proporciona uma série de benefícios à aplicação, entre eles maior velocidade na recuperação dos dados e menor volume de informações trafegadas.

Gostou do nosso guia inicial sobre o que é o GraphQL? Então, confira este outro guia imperdível sobre expressões regulares!

0 Shares:
Deixe um comentário
Você também pode gostar