NodeJS, Apollo Server e GraphQL parte 1

Leonardo Turbiani
Training Center
Published in
7 min readFeb 19, 2018

Desenvolvendo uma API com essa sopa de letrinhas 😜

Aproveitando o combo ressaca do ano novo e carnaval, comecei a colocar minhas metas de 2018 em TO DO, e a primeira a ir para IN PROGRESS é esse artigo =)

Vou tentar aqui passar um pouco do que aprendi construindo o back-end da minha Startup, a Cotabox, em GraphQL junto com o Johnny Cavalcante.
Irei dividir os artigos em 2 ou 3 partes (ainda não defini 100%), mas o propósito é irmos evoluindo aos poucos.

O que é GraphQL ?

GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn’t tied to any specific database or storage engine and is instead backed by your existing code and data.

http://graphql.org/learn/

Colocando meu ponto de vista aqui, sempre que me perguntam o que é GraphQL digo que é algo como um SQL para sua API, pois agora você, consumidor da API, envia instruções que serão interpretadas pelo GraphQL, em um formato específico. É o mesmo que acontece quando um banco de dados interpreta sua instrução SQL. Isso lhe permite mais controle no que é enviado e recebido da API. Por exemplo, no mundo de APIs REST, é comum vermos endpoints como /usarios, /clientes, /usuario/:id e etc. Normalmente esses endpoints nos devolvem um JSON, que pode ser utilizado para exibir dados em uma tela, ou para qualquer outra funcionalidade do nosso software.

Dentro desse contexto de APIs REST, é muito comum casos onde o front-end só precisava do nome e cpf do usuário e descartou todo o JSON vindo do /usuario/:id. Ou as vezes é necessário algo mais composto, como dados do usuário combinado com seus pedidos realizados. Nesse caso em específico, o back-end pode desenvolver um novo endpoint que lhe traga esses dados combinados. Ou você pode combinar chamadas à um ou mais endpoints a fim de lhe atender.
Esse cenário a longo prazo pode nos levar à um caos de endpoints. E isso que eu nem mencionei algo que é bem corriqueiro: a documentação disso tudo. Quem nunca começou uma API REST bonitinha, documentando no Swagger e depois na correria deixou de atualizá-la? — kkkkk

GraphQL ajuda nessas treta aí? Sim! Como agora nós criaremos uma API que interpreta suas instruções, a primeira coisa que acabará é esse monte de endpoint. Você vai determinar um endpoint só, que receberá no body da requisição sua instrução GrahpQL. Outra coisa muito bacana que você ganha, assim que começa a usar GraphQL, é que para ele saber interpretar os comandos enviados na requisição, você precisa definir o GraphQL Schema.
E ao defini-lo, automaticamente documentamos toda nossa API, pois esse Schema é acessível aos clients e nele estão todos nossos métodos e objetos expostos na API. Pode pensar nele como os antigos XMLs usados nas integrações SOAP.

Conceitos básicos

Para que seja possível darmos continuidade nesse artigo e desenvolver nossa API GrahpQL, será necessário entendermos alguns conceitos que fazem parte do cerne do GraphQL. São eles: GraphQL Schema Definition, Query, Mutation, Subscriptions, Types e Resolvers. Sendo que Query, Mutation, Subscriptions* e Types irão compor o nosso GraphQL Schema Definition, então bora lá 😝

Deixarei para falar de Subscriptions nos próximos artigos.

Types

Uma coisa importantíssima de ressaltar é que o GraphQL é fortemente tipado, e com isso, através dos Types é que iremos estruturar a nossa API, desde a definição dos nomes e parâmetros das Queries e das Mutations. Até a granularidade mais fina da nossa API, que são os tipos de dados que trafegam na camada do GraphQL. Vejamos um exemplo:

Aqui definimos um tipo User, com atributos nome e email. Todos nós já vimos isso antes, de forma muito similar, na construção de Classes em Orientação a Objetos. User representará o nosso userModel.

Como na maioria das APIs, queremos trabalhar com nossos models. Então vamos definir 2 tipos bem importantes no GraphQL, que são conhecidos como root types. São eles: Query e Mutations . Esses tipos definem os entry points de uma API GraphQL.

Query

Vamos a um exemplo onde quero buscar um usuário através do email informado, que é algo bem comum em APIs.

Estamos adicionando ao nosso root type Query o método getUserByEmail, que recebe um parâmetro email, do tipo String, e retorna um dado do tipo User, que definimos a pouco.

Vamos para um exemplo de chamada:

Note que após o último parênteses, eu abro e fecho chaves passando um atributo name. Isso significa que do objeto User retornado em getUserByEmail, só quero receber o campo name. Vejamos o retorno:

E se eu quiser o resto do User, ou seja, o email dele ? Só incrementar o atributo email .

E temos como retorno:

Lembra que no começo do artigo eu disse que o GraphQL nos daria maior controle ao que era enviado e recebido à API ? Então eis aqui um exemplo desse controle. Como dei de exemplo também no começo do artigo, se estivéssemos consumindo uma API REST, não haveria essa possibilidade de escolher o que o Server nos entrega.
Esse controle que os clients GraphQL podem usufruir evita o que chamamos de Overfetching e Underfetching. Caso queira ir mais a fundo, segue dois links bem bacanas sobre o assunto: GraphQL is the better REST e GraphQL at the REST-aurant.

Mutations

Outra coisa bem importante além de consultar dados em uma API, é poder criá-los ou alterá-los. No GraphQL as operações relacionadas a Create, Read, Update e Delete do famoso CRUD, são chamadas de Mutations.

Vejamos um exemplo:

Adicionamos ao nosso root type Mutation o método createUser , que recebe name e email que são do tipo String . Note que após a palavra String exite um ponto de exclamação. Isso vai indicar ao GraphQL que esses dois campos são obrigatórios!

Vamos a um exemplo de chamada desse método:

Após o último parênteses eu passo um objeto com os atributos name e email da mesma forma que fizemos nogetUserByEmail, significando assim que queremos apenas esses dois atributos do retorno que o Server nos dará.
Esse comportamento similar foi possível porque na assinatura do método createUser nós especificamos, através dos dois pontos após o fechamento do parênteses, que o retorno da chamada do método seria um objeto do tipo User.

Ao executarmos temos como retorno:

Agora vamos dar vida a essa API GraphQL =)

Resolvers

Como é bem dito neste artigo do Nikolas Burk, podemos pensar na nossa API GraphQL em duas partes, a estrutural e a de comportamento. A parte estrutural se refere ao schema que definimos até então, determinando assim as capacidades da nossa API. Para que essa estrutura funcione, precisamos atribuir um comportamento a cada item que definimos no schema, ou seja, precisamos ensinar ao GraphQL como “resolver” nossas Queries, Mutations e Types. Então vamos Resolvers nossa API 😛

Lembra da nossa Query ?

Nosso root type Query tem um field chamando getUserByEmail que recebe por parâmetro um atributo email do tipo String e retorna um objeto do tipo User .

Vejamos como seriam os resolvers para CADA elemento do nosso exemplo:

Neste exemplo vamos considerar que findUserByEmail irá trazer do banco um objeto do tipo User, ou seja, um objeto JavaScript com atributos name eemail .

Agora vamos entender o que significam esses quatro parâmetros:

  • root: Objeto que contém o valor inicial da cadeia de chamada dos resolvers, o que conhecemos como resolver chain. O valor inicial no começo da cadeia é null . Não entendeu nada? Explicarei logo abaixo**
  • args: Os parâmetros passados para query, no nosso caso o atributo email .
  • context: Objeto que transitará por todos os resolvers da API, permitindo que seja lido ou alterado por acada resolver. Esse objeto de contexto normalmente pode conter dados como usuário logado, acesso ao banco de dados ou qualquer dado que queira compartilhar com seus resolvers.
  • info: Uma representação AST da query ou mutation send resolvida. Este artigo é bem legal, e entra melhor nesse assunto: Demystifying the info Argument in GraphQL Resolvers.

**Como dito anteriormente, o GraphQL irá chamar o resolver correspondente para CADA item a ser resolvido pela API. No nosso caso, ele procurará alguém para resolver o atributo getUserByEmail , que por sua vez devolve um objeto do tipo User que também precisa ser resolvido, o que significa que é ensinar ao GraphQL como buscar os valores de name e email.
Essas chamadas de resolvers são encadeadas, é o que chamamos de resolver chain. Pense nisso com um encadeamento de Promises, onde o retorno de um é a entrada de outro. O fluxo no nosso caso ficaria assim:

  1. GraphQL encontra o resolver do getUserByEmail executa a função context.db.findUserByEmail(args.email) que retorna um objeto do tipo User(um objeto JS com atributos name e email). Como é o primeiro resolver da cadeira, o atributo root tem o valor null.
  2. Agora precisamos resolver o tipo User, e nesse caso o valor de root será o objeto User que o primeiro resolver nos trouxe do banco de dados. Sendo assim ficou fácil, basta associarmos root.name e root.email em seus respectivos atributos.
  3. Finalizado a cadeira de resolvers, o resultado é enviado ao client encapsulado em um atributo chamado data.

Segue alguns links bem interessantes que abordam esse comportamento dos resolvers: Query execution, Root fields & resolvers, Resolver obj argument, Resolver function signature e spec GraphQL.

Conclusão

Bom galera, por hora é isso que eu queria passar como parte 1 do artigo. Uma leve pincelada sobre alguns conceitos do GraphQL. Para parte 2 irei abordar um conceito super importante, as Subscriptions. Que é uma maneira de você receber algo como uma push notification do server a respeito de alguma mudança de estado no back-end. Também começarei montando uma API do zero em NodeJS, aplicando o que aprendemos até aqui + o conteúdo da parte 2. Atualizo vocês em breve!

Tamo junto! Compartilhar sempre =)

--

--