Last active
August 10, 2018 16:48
-
-
Save lucasconstantino/31ee4f2a0ec089767fe8f2c3d89b3dcd to your computer and use it in GitHub Desktop.
GraphQL - pt-br
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 | |
} | |
} | |
} | |
` | |
``` | |
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. | |
 | |
> 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 | |
} | |
} | |
} | |
` | |
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