Skip to content

Instantly share code, notes, and snippets.

@lucasconstantino
Last active August 10, 2018 16:48
Show Gist options
  • Save lucasconstantino/31ee4f2a0ec089767fe8f2c3d89b3dcd to your computer and use it in GitHub Desktop.
Save lucasconstantino/31ee4f2a0ec089767fe8f2c3d89b3dcd to your computer and use it in GitHub Desktop.
GraphQL - pt-br
# GraphQL hoje usando Apollo em aplicações que ainda dependem de APIs REST
Apesar do entusiamo das pessoas que já estão usando GraphQL, a popularidade da ferramenta está [crescendo a passos curtos](https://trends.google.com/trends/explore?date=2014-03-14%202017-03-14&q=GraphQL). Desenvolvedores trabalhando no client-side das aplicações são os que mais rapidamente têm a ganhar com o GraphQL, mas poucos ainda conseguem justificar o investimento financeiro na migração de um backend em pleno funcionamento servindo uma API REST. O que poucos percebem, porém, é que não é preciso fazer a migração simultaneamente no servidor antes de começar a usar a tecnologia no client-side. A [implementação de referência](https://github.com/graphql/graphql-js) para servidores GraphQL é escrita em JavaScript, roda muito bem em navegadores, e é ainda mais fácil de usar quando combinada com as ferramentas fornecidas pelo [Apollo](http://www.apollodata.com/).
> Se você prefere Relay, deveria ler [esse post](http://graphql.org/blog/rest-api-graphql-wrapper/) no blog oficial do GraphQL.
## O que é Apollo?
O GraphQL é, fundamentalmente, apenas um protocolo de comunicação, e portanto existem [dezenas de projetos em várias linguagens](https://github.com/chentsulin/awesome-graphql), tanto pra client-side quanto pra server-side. Já o [Apollo](http://www.apollodata.com/) é um conjunto de ferramentas e produtos criados pelo [time de desenvolvimento do Meteor](https://jobs.lever.co/meteor) para trabalhar com GraphQl.
Dentre esses projetos, há o [graphql-tools](https://github.com/apollographql/graphql-tools), que visa facilitar a criação de schemas executáveis, e o [apollo-client](https://github.com/apollographql/apollo-client), que se auto-determina "*O cliente GraphQL totalmente preparado para produção e para qualquer servidor ou framework UI*". Ousado, não?
## Resolvendo GraphQL queries no navegador
O primeiro problema a ser resolvido é como executar GraphQL resolvers no client-side. Sinceramente, não é muito difícil. Como mencionei anteriormente, o `graphql-js` funciona muito bem no ambiente de um navegador, e basta usá-la como faríamos num servidor Node.
### Instalação
Vamos precisar inicialmente de duas ferramentas para construir nosso schema:
```sh
yarn add --save graphql graphql-tools
```
> Sentido falta do NPM no comando acima? Sugiro que você dẽ uma olhada no [Yarn](https://yarnpkg.com/pt-BR/) ;)
### Construindo o GraphQL Schema
Vamos começar pelo início (!). Construir um schema é simples, usando o [graphql-tools](https://github.com/apollographql/graphql-tools). Começamos por definir um schema usando a [linguagem de schema do GraphQL](http://graphql.org/learn/schema/#type-language), como segue:
```js
const typeDefs = `
type Query {
helloWorld: String!
}
schema {
query: Query
}
`
```
O que estamos dizendo aqui é que nosso schema tem um único typo, chamado *Query*, e que esse tipo é o "tipo raíz". Isso significa que os campos desse tipo são pesquisáveis no primeiro nível do schema - neste caso, o campo `helloWorld`, que é resolvido a uma string.
Em seguida definimos os resolvers através de um objeto que serve de mapa para os campos de cada tipo declarado no schema:
```js
const resolvers = {
Query: {
helloWorld: () => 'Hello!'
}
}
```
Por fim, combinamos a definição do schema com os resolvers usando o método `makeExecutableSchema`, criando assim um schema executável.
```js
import { makeExecutableSchema } from 'graphql-tools'
const schema = makeExecutableSchema({ typeDefs, resolvers })
```
Para manter a simplicidade, por hora vamos manter todo o código num mesmo arquivo, chamado `schema.js`, que, portanto, conterá o seguinte:
```js
import { makeExecutableSchema } from 'graphql-tools'
const typeDefs = `
type Query {
helloWorld: String!
}
schema {
query: Query
}
`
const resolvers = {
Query: {
helloWorld: () => 'Hello!'
}
}
export const schema = makeExecutableSchema({ typeDefs, resolvers })
```
> Há uma mençao extensa sobre [modularização do schema](http://dev.apollodata.com/tools/graphql-tools/generate-schema.html#modularizing) na documentação do Apollo, eu mesmo tenho um projeto sobre este assunto, apesar de ele estar ainda engatinhando: [graphql-modules](https://github.com/lucasconstantino/graphql-modules).
### Executing queries
Agora que temos um schema executável, podemos resolver queries usando o projeto *graphql-js* da seguinte forma:
```js
import { graphql } from 'graphql'
import { schema } from './schema'
const query = '{ helloWorld }'
graphql(schema, query).then(result => {
// Exibe no console:
// {
// data: { helloWorld: "Hello!" }
// }
console.log(result)
})
```
Como se pode ver, primeiro importamos o schema criado no passo anterior, em seguida definimos uma query simples, e por fim executamos ela contra o schema usando o helper *graphql*. Feito! Conseguimos resolver queries de GraphQL.
> Sugiro agora que acesse o [código de exemplos](https://github.com/lucasconstantino/graphql-apollo-rest-wrap) para este post, disponível no GitHub. Baixe o projeto usando git e acesse a tag *[1-hello-world](https://github.com/lucasconstantino/graphql-apollo-rest-wrap/tree/1-hello-world)* para ver o código até este momento.
### Realizando requisições REST nos resolvers
Para fins de simplicar as coisas, vamos usar uma API REST para testes chamada [JSONPlaceholder](https://jsonplaceholder.typicode.com/). Não é preciso instalá-la, está (quase) sempre disponível, e tem um schema básico de um blog, com posts, usuários, comentários, etc; exatamente o que precisamos pra fazer alguns testes com GraphQL.
Primeiro, vamos atualizar nosso schema pra adicionar novos tipos. Vamos começar com o tipo *Post*, junto de um resolver para *posts*, que inicialmente retornará todos os posts disponíveis na API de testes:
```js
const typeDefs = `
type Post {
id: Int!
title: String
body: String
}
type Query {
posts: [Post]!
}
schema {
query: Query
}
`
```
Agora, atualizamos os resolvers da seguinte forma:
```js
const resolvers = {
Query: {
posts: () => fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json())
}
}
```
> Note que utilizamos a [Fetch API](https://developer.mozilla.org/pt-BR/docs/Web/API/Fetch_API), não disponível em todos os navegadores. No código de referência usamos um polyfill para torná-la disponível: [whatwg-fetch](https://github.com/github/fetch).
Agora podemos consultar posts:
```js
import { graphql } from 'graphql'
import { schema } from './schema'
const query = '{ posts { id, title, body } }'
// Exibe no console:
// {
// data: { posts: [...] }
// }
graphql(schema, query).then(console.log)
```
> Checkpoint: *[2-posts-resolver](https://github.com/lucasconstantino/graphql-apollo-rest-wrap/tree/2-posts-resolver)*
Ok, isso parece legal. E se quiséssemos retornar apenas um post dessa API? Fácil. Eis as adições ao schema e aos resolvers:
```diff
type Query {
posts: [Post]!
+ post (id: Int!): Post
}
...
const resolvers = {
Query: {
+ post: (root, { id }) => fetch(`https://jsonplaceholder.typicode.com/posts/${id}`).
posts: () => fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.j
}
}
```
Essa é uma adição interessante, visto que é a primeira vez que estamos usando variáveis. Para consultar um post com `id = 1`, executaríamos a seguinte query:
```js
const query = `
{
post (id: 1) {
id
title
body
}
}
`
```
> Mais um checkpoint no código de referência: *[3-post-resolver](https://github.com/lucasconstantino/graphql-apollo-rest-wrap/tree/3-post-resolver)*.
Agora, analisando o [endpoint de posts na API de testes](https://jsonplaceholder.typicode.com/posts/) vemos que ela retorna um quarto campo em cada post: o `userId`. è chegada a hora para...
### Resolvendo relacionamentos
Relacionamentos são a beleza do GraphQL mas, apesar da sua importância, fundamentalmente são apenas campos comuns. Vamos seguir adiante e adicionar o tipo *User* e o campo *author* no tipo *Post* (continuarei colocando apenas os diffs, visto que os arquivos estão se tornando cada vez maiores):
```diff
type Post {
id: Int!
title: String
body: String
+ author: User!
+ }
+
+ type User {
+ id: Int!
+ username: String
+ email: String
+ }
+
type Query {
posts: [Post]!
post (id: Int!): Post
```
> Nota: a API de testes retorna muito mais campos em cada usuário, mas adicionaremos aqui somente alguns necessários e outros como exemplo.
Agora as coisas estão ficando interessantes. E se quiséssemos trazer o post com id 1, mas também os dados do seu autor? A query é bem simples com GraphQL:
```js
const query = `
{
post (id: 1) {
id
title
body
author {
id
username
email
}
}
}
`
```
E o resolver para esse relacionamento pode ser escrito dessa forma:
```js
const resolvers = {
Query: {
post: (root, { id }) => fetch(`https://jsonplaceholder.typicode.com/posts/${id}`).then(res => res.json()),
posts: () => fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json())
},
Post: {
author: ({ userId }) => fetch(`https://jsonplaceholder.typicode.com/users/${userId}`).then(res => res.json()),
}
}
```
> E, claro, mais um checkpoint: *[4-author-resolver](https://github.com/lucasconstantino/graphql-apollo-rest-wrap/tree/4-author-resolver)*.
A, isso é fantástico! Uma pausa para o café...
### Mutações, por favor!
Hum, na verdade não. Mutações no GraphQL são basicamente mais campos e resolvers, algo muito similar ao que já fizemos até agora. Não vamos falar sobre isso aqui, pois temos mais coisas para cobrir:
## Apollo Client
Ok, entendo que executar queries estáticas se provou fácil, mas nossa aplicação precisará de mais. O próximo passo é integrar o que temos ao [Apollo Client](https://github.com/apollographql/apollo-client).
## Installation
```sh
yarn add apollo-client graphql-tag
```
### Criando o client
Para criar uma instancia do Apollo Client, instanciamos a classe `ApolloClient` fornecendo um objeto de configuração contendo, pelo menos, um *network interface*, que será utilizado pelo client para realizar requisições GraphQL. Normalmente, numa aplicação com GraphQL em ambos client-side e server-side, criamos um *network interface* usando o helper *createNetworkInterface*, que basicamente cria uma interface de rede para realizar requisições POST contra um backend servido no mesmo domínio, no endpoint `/graphql`, por exemplo. Seria algo assim:
```js
import ApolloClient, { createNetworkInterface } from 'apollo-client'
export const client = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'https://graphql.example.com',
}),
})
```
E, para executar uma query, faríamos:
```js
const query = gql`
query {
helloWorld
}
`
client.query({ query }).then(console.log)
```
> Se tiver interesse, leia mais sobre a [camada de network](http://dev.apollodata.com/core/network.html) do Apollo Client.
Aqui, porém, não temos GraphQL no backend, e portanto vamos criar uma interface de rede personalizada para resolver as queries diretamente no navegador. Não é algo simples. Veja só:
```js
import ApolloClient, { printAST } from 'apollo-client'
import { graphql } from 'graphql'
import { schema } from './schema'
export const client = new ApolloClient({
networkInterface: {
query: req => {
const query = printAST(req.query)
const { operationName, variables = {} } = req
return graphql(schema, query, null, null, variables, operationName)
}
}
})
```
Céus o que está acontecendo aqui?
Priemrio, instanciamos o ApolloClient passando nosso *networkInterface* personalizado. Ele consiste de um objeto com o método *query* disponível. Esse método será chamado toda vez que uma query for ser resolvida. O método recebe um único argumento: um objeto do tipo [Request Interface](https://github.com/apollographql/apollo-client/blob/master/src/transport/networkInterface.ts#L32).
Segundo, usamos um helper disponibilizado pelo próprio *apollo-client* para processar o objeto de requisição e criar uma query GraphQL válida, similar as que estavamos definindo antes na mão.
Terceiro, extraímos outras informações importantes da requisição: [`operationName`](http://graphql.org/learn/queries/#operation-name), que é o nome (opcionalmente) dado á operação; e possíveis [`variables`](http://graphql.org/learn/queries/#variables) que foram fornecidas junto da query.
Por último, executamos a query contra o schema, fornecendo também um root inicial, um contexto (nulo aqui, já que não precisamos disso ainda), as variáveis, e o nome da operação. A maioria dos argumentos aqui é opcional.
![Trump is just amazed](https://m.popkey.co/5ea88b/LlVA6.gif)
> Se tiver dúvidas sobre esse último passo, dê uma olhada na documentação oficial sobre [execução de queries](http://graphql.org/learn/execution/).
Agora podemos usar nosso client como normalmente faríamos:
```js
import { client } from './client'
import gql from 'graphql-tag'
const query = gql`
query Post ($id: Int!) {
post (id: $id) {
id
title
body
author {
id
username
email
}
}
}
`
client.query({ query, variables: { id: 1 } }).then(console.log)
```
> Último checkpoint: *[5-apollo-client](https://github.com/lucasconstantino/graphql-apollo-rest-wrap/tree/5-apollo-client)*.
## Conclusão
Isso é tudo. Espero que vocês tenham apreciado nosso devaneio no aprendizado de GraphQl e, sobretudo, espero que vocês agora sejam capazes de começar a usar GraphQL, sem mais desculpas envolvendo o pessoal do backend que estar com preguissa de fazer um servidor pra você.
### Cena após os créditos:
Se você está realmente só começando com GraphQL talvez você nem saiba como/onde usar esse cliente que acabamos de criar. Foi mal. Bom, eu imagino que se você está aqui é provável que já use React, Angular, ou mesmo Vue (se for um desenvolvedor hispter incompreendido). Se for esse o caso, tem algumas bibliotecas que vão te ajudar a seguir em frente:
- https://github.com/apollographql/react-apollo
- https://github.com/apollographql/apollo-angular
- https://github.com/Akryum/vue-apollo
Até mais!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment