O documento apresenta GraphQL, uma linguagem de consulta para APIs. Aborda os seguintes tópicos:
1) Motivações como resolver problemas de requisições REST desnecessárias;
2) Características como linguagem declarativa de consulta e funções "resolve";
3) Exemplos de queries, mutações, variáveis e fragmentos.
2. Sobre mim
➔Desenvolvedor Web desde ~2005
➔Formado na USP de São Carlos
➔Full Stack Developer na startup Easy Carros
➔Hackathons
◆ 1º lugar - Hackathon Globo 2016
◆ 1º lugar - MasterCard Code4Inclusion Miami
◆ 2º lugar - Masters of Code São Paulo
◆ 1º lugar - Destination Hack
◆ 1º lugar - API Hackday SP
@brunolemos
3. O que iremos abordar?
1. Motivação // que problemas resolve?
2. Características // query language, ...
3. Queries & Mutations // query { user(id: 1) { name } }
4. Na prática // adicionando GraphQL à uma API já existente
5. Autenticação // segurança
6. Client & Libs // relay, apollo, …
7. Próximos passos // o que não abordamos + futuro do graphql
7. Usando REST
GET /v1/me { _id: 1, friends: [2,
3, 4,5] }
GET /v1/users/2/posts/last {_id: ‘post_2a’}
GET /v1/users/3/posts/last {_id: ‘post_3a’}
GET /v1/users/4/posts/last {_id: ‘post_4a’}
GET /v1/users/5/posts/last {_id: ‘post_5a’}
1. Motivação
Muitas requisições… Já sei, vou criar um endpoint para isso.
8. GET /v1/myFriendsLastPosts [{_id: 1, lastPost: {...}, {_id: 2, lastPost:
{...}]
Usando “REST”
E se eu quiser obter as páginas que meus amigos curtiram?
1. Motivação
9. E se eu quiser obter as páginas que meus amigos curtiram e os últimos posts?
GET /v1/myFriendsLikedPages [{_id: 1, pages: [...]}, {_id: 2, pages: [...]}]
Usando “REST”
1. Motivação
10. GET /v1/myFriendsLikedPages [{_id: 1, pages: [...]}, {_id: 2, pages: [...]}]
GET /v1/myFriendsLastPosts [{_id: 1, lastPost: {...}}, {_id: 2, lastPost:
{...}}]
Usando “REST”
1. Motivação
// faço o merge dos resultados no client
[{_id: 1, lastPost: {...}, pages: [...]}, ...]
// já sei! que tal um novo endpoint?
GET /v1/myFriendsLastPostsAndPages
👎👎👎👎👎👎
11. No REST, é fácil você se encontrar criando endpoints para retornos específicos.
Isto não é escalável.
1. Motivação
12. Além disso…
Ao fazer um GET em um endpoint, que dados serão retornados? #surprise
Como descobrir:
1. Fazer uma requisição de teste
2. Ler a documentação (pode estar desatualizada)
3. Ler o código
Dados retornados:
1. Provavelmente muito mais do que você precisa
1. Motivação
13. “Analisamos alternativas, como o REST. (...) Ficamos frustados com as diferenças entre os
dados que queríamos e as requests que eram necessárias para obtê-los.”
2012
Lee Byron, Facebook Software Engineer
1. Motivação
14. GraphQL é criado pelo Facebook
Usado apenas internamente
2012
1. Motivação
17. O client declara os dados que precisa e a resposta é um espelho da entrada
“Retorne isto. Nada mais, nada menos.”
Declarative query language
//REQUEST
query {
user(_id: “xxx”) {
_id
name
email
}
}
//RESPONSE
{
"data": {
"user": {
"_id": "xxx",
"name": "Bruno Lemos",
"email": "brunohplemos@gmail.com"
}
}
}
Características
18. Funções “resolve”
Visão geral
Características
Resposta em JSON
no mesmo formato
da entrada
Entrada dos dados
que precisa
Camada do GraphQL
Diferentes clients Bancos de dadosServidor
Retorno da função “resolve”
com os dados desejados
19. Na função resolve, você é livre para pegar o dado de onde quiser
// Funções “resolve” são as responsáveis por dizer onde pegar os dados.
// Podem retornar dados de qualquer lugar, desde que retorne o valor final ou uma Promise.
// Exemplos para query { user(_id: “xxx”) { name } }
resolve: (root, args, context) => ({ name: ‘Bruno Lemos’, outroCampo: ‘X’ }), // Dado arbitrário
resolve: (root, args, context) => User.findById(args._id), // Método que retorna uma promise
resolve: (root, args, context) => fetch(‘http://api.site.com/v1/user’), // API externa
Características
Funções “resolve”
23. Query: Várias ao mesmo tempo
Sintaxe
query {
user(_id: “xxx”) {
_id
name
}
vehicle(_id: “tesla_model_s”) {
brand
model
}
}
{
"data": {
"user": {
"_id": "xxx",
"name": "Bruno Lemos"
},
"vehicle": {
"brand": "Tesla",
"model": "Model S"
}
}
}
24. Query: Várias ao mesmo tempo
Sintaxe
query {
user(_id: “xxx”) {
_id
name
}
user(_id: “xxx”) {
github
}
}
{
"data": {
?
}
}
25. Query: Várias ao mesmo tempo
Sintaxe
query {
user(_id: “xxx”) {
_id
name
}
user(_id: “xxx”) {
github
}
}
{
"data": {
"user": {
"_id": "xxx",
"name": "Bruno Lemos",
"github": "brunolemos"
},
}
}
26. Query: Várias ao mesmo tempo
Sintaxe
query {
user(_id: “xxx”) {
_id
name
}
user(_id: “xxx_2”) {
github
}
}
{
"data": {
?
}
}
27. Query: Várias ao mesmo tempo
Sintaxe
query {
user(_id: “xxx”) {
_id
name
}
user(_id: “xxx_2”) {
github
}
}
{
"errors": [{
"message": "Fields "user" conflict
because they have differing arguments. use
different aliases on the fields to fetch both
if this was intentional."
}]
}
35. Query
Ok, o campo name é sempre String, o campo age é sempre Int, …
E se eu tiver um campo que possa retornar mais de um tipo?
Exemplo: campo user que pode ser tanto do tipo User quanto Admin
36. query {
me {
__typename
... on Admin {
name
}
... on User {
name
age
}
}
}
Query: Múltiplos tipos
Sintaxe
{
"data": {
"me": {
"__typename": "Admin",
"name": "Bruno Lemos"
}
}
}
A query ‘me’ pode retornar um tipo diferente
dependendo de quem está logado no momento
37. query {
me {
__typename
... on Admin {
name
}
... on User {
name
age
}
}
}
Query: Múltiplos tipos
Sintaxe
{
"data": {
"me": {
"__typename": "User",
"name": "Bruno Lemos",
"age": 23
}
}
}
A query ‘me’ pode retornar um tipo diferente
dependendo de quem está logado no momento
38. Ok, chega de query
Se as queries são como o GET do REST, como fazer o POST / PUT / DELETE?
39. Mutation
Mutations são como o POST / PUT / DELETE do REST:
Você usa quando haverá alteração nos dados.
[POST] /v1/users
[PUT] /v1/users/1
[DELETE] /v1/users/1
addUser(name: “Mateus”)
updateUser(_id: 1, name: “Matheus”)
deleteUser(_id: 1)
47. Vamos criar uma query que receba um argumento _id e retorne o usuário
correspondente.
Na prática
48. // Vamos usar Node.js
// Dependências:
$ npm i -S express graphql express-graphql
Antes de tudo...
Servidor
49. index.js
const express = require('express');
const app = express();
const server = app.listen(process.env.PORT || 3000, () => {
const { address, port } = server.address();
console.log(`Running at http://${address}:${port}`);
});
Criando o servidor
const graphqlHTTP = require('express-graphql');
const schema = require('./schema'); //será criado em breve
app.use('/graphql', graphqlHTTP({ schema, graphiql: true }));
50. ./user/type.js
export default new GraphQLObjectType({
name: 'User',
fields: {
_id: { type: new GraphQLNonNull(GraphQLID) },
name: { type: GraphQLString },
},
});
Precisamos criar o tipo Usuário, que será o retorno da query
Tipos já existentes: ID, String, Int, Boolean, Object, Enum, List, … (ver lista completa)
Criando o servidor
51. ./user/query.js
Cada Query é um objeto comum
Define o tipo de retorno, os argumentos de entrada e a função “resolve”
Criando o servidor
// bluebird converte uma funcao que pussui callback em uma promise
const getUserAsync = Bluebird.promisify(myMethodFromOldApiThatUsesCallback);
export default {
type: UserType, // arquivo criado anteriormente
args: {
_id: { type: new GraphQLNonNull(GraphQLID) },
},
resolve: (root, args, context) => getUserAsync(args._id), // onde a mágica acontece
};
52. schema.js
export default new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQuery',
fields: {
user: UserQuery, // arquivo criado anteriormente
// users: ..., // aqui iria as outras queries
// posts: ...,
},
}),
mutation: ..., // definirá todas as mutations existentes (mesma sintaxe acima)
});
Schemas possuem uma RootQuery e uma RootMutation
Criando o servidor
67. GraphiQL =
Documentação automática, execução de queries, autocomplete, … É como ter um Graph API Explorer próprio!
https://developers.facebook.com/tools/explorer
75. Server
GraphQL não é apenas para Node.js.
Existem implementações para Ruby, PHP, Go, Python, Haskell, ...
Client
Relay (react)
Apollo (react, angular, ios swift, ...)
Libs
76. Libs
Utils
Graffiti (usar schema do Mongoose)
Model Visualizer (converta seu schema em um diagrama)
Services
Reindex (backend as a service)
Lista completa: Awesome GraphQL