GraphQL — Aprendendo na prática

Evandro F. Souza
Training Center
Published in
6 min readNov 27, 2018

Recentemente fiz um post introdutório sobre GraphQL, nele compartilhei um pouco sobre o que é esta ferramenta, quais seus pontos fortes e fracos. A ideia deste post é dar continuidade nestes estudos, aqui vamos desenvolver um pequeno servidor GraphQL utilizando a linguagem Go.

Primeiro, alguns conceitos GraphQL

Antes de irmos para a prática, quero falar um pouco sobre alguns conceitos da linguagem GraphQL: schema, query e mutations.

Schema

Um schema está no centro de qualquer implementação de GraphQL. Ele descreve a funcionalidade disponível para os clientes que se conectam a ele. Eu gosto de pensar nele como um contrato.

Na elaboração de um schema, um dos principais blocos utilizados é o type. O type fornece uma ampla gama de funcionalidades dentro de um schema, incluindo a capacidade de:

  • Criar relacionamentos entre outros types (por exemplo, entre os tipos item e pedido).
  • Definir quais operações de busca (query) e manipulação (mutation) de dados podem ser executadas pelo cliente.
  • Auto-explicar quais recursos estão disponíveis para o cliente (via introspecção).

No exemplo abaixo é possível observar um relacionamento entre types:

Query e Mutations

O GraphQL tem dois types que são especificamente utilizados para definir como é feita a consulta e a manipulação de dados.

O type Query serve para definir o contrato de consulta de dados. A funcionalidade dele é comparável com o verbo GET de APIs REST. Abaixo é possível visualizar um exemplo do type Query.

O type Mutation serve para definir o contrato de manipulação de dados. A funcionalidade dele é comparável com a dos verbos POST, PUT, PATCH e DELETE de APIs REST. Abaixo é possível visualizar um exemplo do type Mutation:

O que foi descrito acima é um resumo com o que é preciso entender para acompanhar esse post. Para mais detalhes sobre a sintaxe da linguagem GraphQL acesse aqui.

Obs: Durante meus estudos, instalei esta extensão na IDE VSCode e ajudou bastante no aprendizado e produtividade.

Mãos a obra

Agora sim vamos lá! Vamos desenvolver um servidor GraphQL.

O que será o essa aplicação?

A aplicação que criaremos será um servidor GraphQL para um sistema de organização de estudos. É um sistema que pretendo fazer para organizar meus estudos semanais, atualmente eu uso o Google docs para isso.

Se bateu a curiosidade e quiser mais detalhes, descrevo essa metodologia de organização de estudos neste post.

Para ficar mais fácil entender o quais informações este servidor receberá ou retornará, fiz um simples wireframe, ilustrado na figura abaixo:

Figura 1 — exemplo do sistema para organização estudos

A escolha da biblioteca

Assim como qualquer linguagem, o Go possui diversas opções diferentes de bibliotecas implementando o GraphQL. Abaixo listo as que encontrei durante as pesquisas:

  1. https://github.com/graphql-go/graphql
  2. https://github.com/graph-gophers/graphql-go
  3. https://github.com/samsarahq/thunder
  4. https://github.com/playlyfe/go-graphql
  5. https://github.com/vektah/gqlgen

Como estou aprendendo GraphQL, os principais fatores pesados durante a escolha da biblioteca foram:

  1. Variedade de exemplos.
  2. Documentação detalhada .
  3. Simplicidade.
  4. Utilizar o schema GraphQL.

Após uma rápida analise, a biblioteca escolhida foi a graph-gophers/graphql-go. Durante a pesquisa, achei um repositório com um ótimo exemplo e é nele que iremos nos inspirar para criar nosso servidor.

#ShowMeTheCode

O código completo deste tutorial está neste repositório.

Para começar a entender, gostaria de explicar como foi feita a organização do código:

.
├── schema.graphql -> É o schema da aplicação
├── server.go -> É o Main da aplicação
├── db.go -> Contém a inicialização do GORM.
├── resolvers.go -> Contém a implementação dos métodos do schema
|
├── learning_topic_model.go -> Contém o modelo ORM
├── learning_topic_resolver.go -> Contém a implementação do schema
|
├── reference_db.go -> Contém métodos de acesso ao DB
├── reference_model.go -> Contém o modelo ORM
├── reference_resolver.go -> Contém a implementação do schema
|
├── study_db.go -> Contém métodos de acesso ao DB
├── study_model.go -> Contém o modelo ORM
└── study_resolver.go -> Contém a implementação do schema

Note que destaquei de negrito os arquivos que são relacionados a implementação GraphQL. Os demais são basicamente arquivos de configuração e acesso ao banco de dados.

Primeiro, vamos analisar o schema.graphql :

Este schema possui uma Query e uma Mutation. A Query permite consultar o estudo pelo seu ID. A Mutation permite inserir um novo estudo. Note que o parâmetro do Mutation, o StudyInput, é um input (e não um type). Outro ponto que é interessante citar aqui é o uso do sinal de exclamação. Na linguagem GraphQL, ele serve para definir um campo não-nulo. No caso do exemplo estou usando para ilustrar que os campos são obrigatórios.

Certo, agora onde utilizamos esse schema? No arquivo server.go o schema.graphql está sendo interpretado:

Neste arquivo estamos subindo um servidor HTTP e ele possui dois endpoints:

  • http://localhost:8080/: É pagina principal do nosso servidor. Responsável por renderizar o graphiQL( é uma IDE GraphQL para o browser ). A variável page está com o HTML desta ferramenta.
  • http://localhost:8080/query: Este é o endpoint utilizado para executar o comandos GraphQL. Ou seja, qualquer comando (query ou mutation) solicitado para nosso servidor passará por esse endpoint.

No momento que o método MustParseSchema é executado, será validado se todos os métodos e campos definidos no schema estão implementados no struct Resolver.

Se você clonou o projeto, faça um teste, modifique o arquivo schema.graphql e adicione uma nova Query: getStudies():[Study] . Ao executar você verá o erro: panic: *main.Resolver does not resolve “Query”: missing method for field “getStudies”

A implementação destes métodos está no arquivo resolvers.go :

O type retornado e todos os seus relacionados devem implementar um método para cada campo definido no schema. É isso que está sendo feito nos arquivos study_resolver.go, reference_resolver.go e learning_topic_resolver.go. Para não ficar muito longo o post, vamos analisar somente o study_resolver.go:

Os métodos estão com as letras todas maiúsculas pelo simples fato que o objeto Study é o mesmo objeto utilizado pelo ORM e ele já possui propriedades com os mesmos nomes. Outra alternativa seria criar um struct somente para o GraphQL e fazer uma conversão entre o objeto do ORM e o do GraphQL.

Usando o GraphiQL para consultar e inserir dados

Certo, agora vamos executar o projeto e fazer as consultas com GraphQL.

Para executar utilize o comando abaixo:

go run *.go

Após executar, acesse http://localhost:8080 e verá uma tela assim:

Figura 2 — GraphiQL

Clicando no botão “Docs”(canto superior direito da tela), é possível navegar pela documentação dinâmica e entender o que esse servidor está entregando.

Ao começar a digitar, aperte ctrl+spacepara usar autocomplete. Imagine agora essa funcionalidade no dia a dia do desenvolvedor front-end (que está consumindo o GraphQL). Realmente, muito útil.

A primeira Query

query {
getStudy(id:1) {
scopeDefinition,
successDefinition,
learningPath {
order,
description
},
references {
url,
category {
name
}
}
}
}

Abaixo a mesma query, porém sendo executada via curl:

curl \
-X POST \
-H "Content-Type: application/json" \
--data '{ "query": "{ getStudy(id: 1) { scopeDefinition successDefinition learningPath { order description } references { url category { name } } }}" }' \
http://localhost:8080/query

A primeira Mutation

mutation {
addStudy (study: {
scopeDefinition: "scope",
successDefinition: "success"
}), {
id
}
}

Note que a sintaxe da Mutation permite que o retorno seja definido. No caso do exemplo, estamos retornando somente o id.

Conclusão

Após esse tutorial, ficou mais visível os pontos fortes e fracos citados no post anterior. Conversando sobre GraphQL, o Zaquiel Grings me fez refletir sobre o quão fácil (ou difícil) seria migrar uma API REST para GraphQL. Bom, após esse tutorial essa questão ficou mais clara. Considerando que a API REST esteja bem estruturada, acredito que a migração seria fácil. Digo isso principalmente pelo fato do GraphQL ser mais um endpoint(no nosso caso é o /query) da API já existente. Dito isso, o problema fica só em relação à organização e reaproveitamento do código.

Para finalizar, independente da linguagem escolhida, existem diversas bibliotecas GraphQL e algumas não necessitam que o desenvolvedor configure o schema.graphql. Nesses casos, a própria biblioteca gera o schema com base nas classes do projeto. Quando for escolher qual biblioteca utilizar, leve em consideração essa possibilidade também.

Se você quiser continuar aprendendo sobre GraphQL, aconselho assistir as aulas nesse site. O conteúdo é sensacional.

Se quiser trocar uma ideia ou entrar em contato comigo, pode me achar no Twitter(@e_ferreirasouza) ou Linkedin.

Grande abraço e até a próxima!

--

--