Web Dev Drops

Fullstack com Node.js, React e GraphQL  - 6: Servidor GraphQL

avatar
Douglas Matoso
Atualizado em 21/04/2018
Leitura: 7 min.

E aí, pessoal! Neste sexto post da série Fullstack com Node.js, React e GraphQL vamos adicionar o GraphQL no servidor usando Apollo Server.

GraphQL

GraphQL é uma linguagem de consulta (query language) para APIs que permite ao cliente descrever exatamente os dados que quer receber, e também um conjunto de tecnologias que permite ao servidor descrever os dados disponíveis e entregar estes dados, que podem ser buscados de múltiplas fontes (bancos de dados, APIs REST, outros servidores GraphQL).

Para ilustrar, veja um exemplo de query e o retorno do servidor:

Eu pedi pelas corretoras, campos id e nome, e dentro de cada corretora trazer seus investimentos, apenas campo nome. Esta é a parte QL do nome (query language).

E o grafo?

A parte Graph do nome vem do fato que toda busca percorre um grafo.

Grafo é uma estrutura formada por vértices (ou pontos ou nós) e arestas (ou linhas) que ligam os vértices.

Para ilustrar, veja como ficaria o grafo que representa nossa aplicação. Destacado em laranja está o caminho percorrido pela query acima.

Grafo com a representação de nossos modelos e suas relações.

No GraphQL sempre temos um nó especial, chamado Query (ou ponto de entrada, root query, entrypoint, root type) que é por onde toda consulta deve iniciar.

No nosso exemplo, a partir daí a consulta pode requisitar um ou mais Brokers ou Investments, e sequencialmente ir seguindo as arestas e pedindo dados de outros modelos relacionados. Essa estrutura, e o que pode ou não ser pedido será definida por nós, quando criarmos nosso schema.

Apollo

Apollo é uma plataforma para desenvolvimento com GraphQL composta por:

  • Apollo Client: facilita a integração do frontend com o servidor GraphQL, possuindo bibliotecas para os principais frameworks JS (React, Vue, Angular) e plataformas mobile nativas (Android e iOS).
  • Apollo Engine: fornece ferramental auxiliar para infraestrutura como caching, tratamento de erros e rastreamento de performance.
  • Apollo Server: bibliotecas que auxiliam na criação do servidor. Vamos usá-lo nesta parte do projeto.

Schema

A primeira coisa que precisamos pensar é na forma dos dados que serão disponibilizados. Como já temos nossos modelos definidos, vamos expor o que poderá ser consultado através do GraphQL (montar aquele grafo que vimos acima).

O schema é como se fosse um contrato entre o fornecedor e o consumidor dos dados (ou entre o server e o client).

Ponto de entrada

Vamos criar nosso schema em src/graphql/schema.graphql, começando pela root query:

type Query {
  brokers(limit: Int): [Broker]
  broker(id: ID!): Broker
  investments(limit: Int): [Investment]
  investment(id: ID!): Investment
}

Cada atributo possui um nome e um tipo de retorno e, opcionalmente, pode receber parâmetros. A sintaxe se assemelha a assinatura de funções em linguagens fortemente tipadas.

! como em id: ID! significa obrigatoriedade daquele valor.

[] como em [Broker] significa uma lista de objetos daquele tipo.

Resumindo, nosso ponto de entrada permite buscar por um broker ou um investment, informando o id como parâmetro, ou por uma lista de brokers ou investments, opcionalmente informando um limite.

Demais nós

Na sequência descrevemos os nós que representam nossos modelos, ficando assim:

scalar Date

type Query {
  brokers(limit: Int): [Broker]
  broker(id: ID!): Broker
  investments(limit: Int): [Investment]
  investment(id: ID!): Investment
}

type Broker {
  id: ID!
  name: String!
  investments: [Investment]
}

type Investment {
  id: ID!
  name: String!
  broker: Broker
  balanceUpdates(limit: Int, order: [[String]]): [BalanceUpdate]
  transactions: [Transaction]
}

type BalanceUpdate {
  id: ID!
  amount: Float!
  date: Date!
}

type Transaction {
  id: ID!
  amount: Float!
  date: Date!
}

Para mais detalhes, veja a documentação sobre a definição de schema: http://graphql.org/learn/schema/

Resolvers

Definido o schema e a forma do nosso grafo, a aplicação ainda não sabe como buscar os dados para atender as queries. Vamos resolver isso com resolvers. 🥁😁

Vamos criar o arquivo src/graphql/resolvers.js:

const { GraphQLString } = require('graphql')
const { Broker, Investment, BalanceUpdate, Transaction } = require('../models')

module.exports = {
  Query: {
    brokers: (obj, args) => Broker.all(args),
    broker: (obj, { id }) => Broker.findById(id),
    investments: (obj, args) => Investment.all(args),
    investment: (obj, { id }) => Investment.findById(id),
  },
  Investment: {
    broker: (obj) => Broker.findOne({ where: { id: obj.BrokerId } }),
    balanceUpdates: (obj, args) =>
      BalanceUpdate.all({ where: { InvestmentId: obj.id }, ...args }),
    transactions: (obj) => Transaction.all({ where: { InvestmentId: obj.id } }),
  },
  Broker: {
    investments: (obj) => Investment.findAll({ where: { BrokerId: obj.id } }),
  },
  Date: GraphQLString,
}

Este arquivo exporta um objeto JS onde para cada navegação de um nó a outro do grafo definimos uma função que irá buscar os dados referente àquela parte da query (E esta busca pode ser em mútiplos locais: banco de dados, API REST ou até outro servidor GraphQL).

Por exemplo, se estou em Investimento e quero buscar a Corretora daquele investimento, tenho:

Investment: {
  broker: (obj) => Broker.findOne({ where: { id: obj.BrokerId } })
}

O primeiro argumento da função, obj, é o objeto que representa aquele nó. Assim, para buscar o Broker usamos Broker.findOne passando o atributo BrokerId do Investment.

Para queries que aceitam parâmetros, estes são passados no segundo argumento da função. Exemplo:

Query: {
    brokers: (obj, args) => Broker.all(args),
    broker: (obj, { id }) => Broker.findById(id)
}

Endpoint de consultas

Temos o schema, que descreve as queries, e os resolvers que buscam os dados. Vamos juntar as duas partes e criar nosso endpoint de consultas, que é o endereço onde o frontend da aplicação vai bater para trazer os dados para a tela. Aqui vamos usar o Apollo Server:

const { graphqlExpress } = require('apollo-server-express')
const { makeExecutableSchema } = require('graphql-tools')
const { importSchema } = require('graphql-import')
const resolvers = require('./graphql/resolvers')

const setup = (app) => {
  const schema = makeExecutableSchema({
    typeDefs: importSchema('src/graphql/schema.graphql'),
    resolvers,
  })

  // graphql endpoint
  app.use('/graphql', graphqlExpress({ schema }))
}

module.exports = setup

Usei a função makeExecutableSchema do módulo graphql-tools para criar um schema “executável” a partir do nosso schema e resolvers.

Para importar o arquivo schema.graphql usei o módulo graphql-import, já que não é um arquivo JS e não pode ser importado diretamente.

Com o schema “executável”, uso a função graphqlExpress, do módulo apollo-server-express, que é um middleware do Express, para definir o endpoint no caminho /graphql.

Para organização eu deixei este código em um arquivo separado, setupGraphQL.js, que exporta uma função de setup, que é usada no src/index.js esta forma:

const setupGraphQL = require('./setupGraphQL')

const app = express()

setupGraphQL(app)

Agora precisamos testar nossas consultas. Ah, se tivesse uma espécie de playground onde pudéssemos inserir queries e ver os resultados…

Mas tem! É o…

GraphiQL

O GraphiQL é uma interface web que permite inserir queries, possui autocomplete e uma documentação com todas as queries e campos possíveis. Tudo gerado a partir do nosso schema.

Interface do GraphiQL

Para habilitar esta interface basta usar o middleware graphiqlExpress do Apollo, informando o endpoint de consultas.

const { graphqlExpress, graphiqlExpress } = require('apollo-server-express')

app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }))

Assim, basta rodar o servidor local e acessar http://localhost:5000/graphiql para explorar seu servidor GraphQL.

Resultado final

O código do projeto até este ponto está em: https://github.com/doug2k1/my-money/tree/v5.0.0

No próximo capítulo

Na próxima parte vamos implementar autenticação para proteger nosso endpoint de consultas contras bisbilhoteiros.

Stay tuned!

Feedbacks?

E aí, o que está achando até agora? Algo que precisa melhorar?

Comentários

Comentários desabilitados