GraphQL - APIs mais robustas e flexíveis

80
GraphQL APIs mais robustas e flexíveis @brunolemo s

Transcript of GraphQL - APIs mais robustas e flexíveis

Page 1: GraphQL - APIs mais robustas e flexíveis

GraphQL

APIs mais robustas e flexíveis@brunolemos

Page 2: GraphQL - APIs mais robustas e flexíveis

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

Page 3: GraphQL - APIs mais robustas e flexíveis

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

Page 4: GraphQL - APIs mais robustas e flexíveis

1.Motivação

Page 5: GraphQL - APIs mais robustas e flexíveis

Imagine uma aplicação na qual você pode:

1. Adicionar amigos2. Publicar posts3. Curtir páginas

1. Motivação

Page 6: GraphQL - APIs mais robustas e flexíveis

Como você pegaria o último post de cada amigo seu?

1. Motivação

Page 7: GraphQL - APIs mais robustas e flexíveis

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.

Page 8: GraphQL - APIs mais robustas e flexíveis

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

Page 9: GraphQL - APIs mais robustas e flexíveis

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

Page 10: GraphQL - APIs mais robustas e flexíveis

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

👎👎👎👎👎👎

Page 11: GraphQL - APIs mais robustas e flexíveis

No REST, é fácil você se encontrar criando endpoints para retornos específicos. Isto não é escalável.

1. Motivação

Page 12: GraphQL - APIs mais robustas e flexíveis

Além disso…Ao fazer um GET em um endpoint, que dados serão

retornados? #surpriseComo descobrir:

1. Fazer uma requisição de teste2. Ler a documentação (pode estar desatualizada)3. Ler o código

Dados retornados:4. Provavelmente muito mais do que você precisa

1. Motivação

Page 13: GraphQL - APIs mais robustas e flexíveis

“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.”

2012Lee Byron, Facebook Software Engineer

1. Motivação

Page 14: GraphQL - APIs mais robustas e flexíveis

GraphQL é criado pelo Facebook

Usado apenas internamente

20121. Motivação

Page 15: GraphQL - APIs mais robustas e flexíveis

GraphQL liberado para o público (open source)

20151. Motivação

Page 16: GraphQL - APIs mais robustas e flexíveis

Características

Page 17: GraphQL - APIs mais robustas e flexíveis

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": "[email protected]" } }}

Características

Page 18: GraphQL - APIs mais robustas e flexíveis

Funções “resolve”

Visão geralCaracterísticas

Resposta em JSON no mesmo

formato da entrada

Entrada dos dados que

precisaCamada do

GraphQL

Diferentes clients

Bancos de dados

Servidor

Retorno da função “resolve” com os dados desejados

Page 19: GraphQL - APIs mais robustas e flexíveis

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árioresolve: (root, args, context) => User.findById(args._id), // Método que retorna uma promiseresolve: (root, args, context) => fetch(‘http://api.site.com/v1/user’), // API externa

Características

Funções “resolve”

Page 20: GraphQL - APIs mais robustas e flexíveis

Sintaxe

Page 21: GraphQL - APIs mais robustas e flexíveis

Query

Queries são como o GET do REST:

Você usa para obter dados, não podendo fazer mutações.

Page 22: GraphQL - APIs mais robustas e flexíveis

QuerySintaxe

query { user(_id: “xxx”) { _id name }}

{ "data": { "user": { "_id": "xxx", "name": "Bruno Lemos" } }}

Page 23: GraphQL - APIs mais robustas e flexíveis

Query: Várias ao mesmo tempoSintaxe

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" } }}

Page 24: GraphQL - APIs mais robustas e flexíveis

Query: Várias ao mesmo tempoSintaxe

query { user(_id: “xxx”) { _id name } user(_id: “xxx”) { github }}

{ "data": { ? }}

Page 25: GraphQL - APIs mais robustas e flexíveis

Query: Várias ao mesmo tempoSintaxe

query { user(_id: “xxx”) { _id name } user(_id: “xxx”) { github }}

{ "data": { "user": { "_id": "xxx", "name": "Bruno Lemos", "github": "brunolemos" }, }}

Page 26: GraphQL - APIs mais robustas e flexíveis

Query: Várias ao mesmo tempoSintaxe

query { user(_id: “xxx”) { _id name } user(_id: “xxx_2”) { github }}

{ "data": { ? }}

Page 27: GraphQL - APIs mais robustas e flexíveis

Query: Várias ao mesmo tempoSintaxe

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." }]}

Page 28: GraphQL - APIs mais robustas e flexíveis

Query: AliasSintaxe

query { dan: user(_id: “dan_id”) { _id name } arunoda: user(_id: “arunoda_id”) { _id name }}

{ "data": { "dan": { "_id": "dan_id", "name": "Dan Abramov", }, "arunoda": { "_id": "arunoda_id", "name": "Arunoda Susiripala", } }}

Page 29: GraphQL - APIs mais robustas e flexíveis

Query: NestedSintaxe

query { user(_id: “xxx”) { _id name friends(limit: 1) { name } }}

{ "data": { "user": { "_id": "xxx", "name": "Bruno Lemos", "friends": [{ "name": "Dan Abramov" ]} } }}

Page 30: GraphQL - APIs mais robustas e flexíveis

Query: Nested!!!Sintaxe

query { user(_id: “xxx”) { _id name friends(limit: 1) { name friends(limit: 1) { name } } }}

{ "data": { "user": { "_id": "xxx", "name": "Bruno Lemos", "friends": [{ "name": "Dan Abramov", "friends": [{ "name": "Arunoda Susiripala" }], }], }, }}

Page 31: GraphQL - APIs mais robustas e flexíveis

Query: Nested (exemplo inicial)Sintaxe

{ "data": { "user": { "name": "Bruno Lemos", "friends": [{ "name": "Sashko Stubailo", "latestPost": { "title": "GraphQL is the future" }, "pages": [{ "name": "Apollo Client" }], }], }, }}

query { user(_id: “xxx”) { name friends { name latestPost { title } pages { name } } }}

Page 32: GraphQL - APIs mais robustas e flexíveis

Query: Nested + UtilsSintaxe

query { user(_id: “xxx”) { thumbnail: image(size: 100) { url width height } fullPicture: image { url width height } }}

{ "data": { "user": { "thumbnail": { "url": "thumbnail_100x100.jpg", "width": 100, "height": 100 }, "fullPicture": { "url": "picture.jpg", "width": 2048, "height": 2048 } } }}

Page 33: GraphQL - APIs mais robustas e flexíveis

Query: Nested + UtilsSintaxe

query { user(_id: “xxx”) { createdAt { format(format: "DD/MM/YYYY HH:mm") timezone iso timestamp } }}

{ "data": { "user": { "createdAt": { "format": "01/09/2016 19:30", "timezone": "America/Sao_Paulo", "iso": "2016-09-01T22:30:00.000Z", "timestamp": "1472769000000" } } }}

Page 34: GraphQL - APIs mais robustas e flexíveis

Query: Nested + UtilsSintaxe

query { user(_id: “xxx”) { createdAt(timezone: “America/New_York”) { format(format: "DD/MM/YYYY HH:mm") timezone } }}

{ "data": { "user": { "createdAt": { "format": "01/09/2016 18:30", "timezone": "America/New_York" } } }}

Page 35: GraphQL - APIs mais robustas e flexíveis

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

Page 36: GraphQL - APIs mais robustas e flexíveis

query { me { __typename ... on Admin { name } ... on User { name age } }}

Query: Múltiplos tiposSintaxe

{ "data": { "me": { "__typename": "Admin", "name": "Bruno Lemos" } }}

A query ‘me’ pode retornar um tipo diferentedependendo de quem está logado no momento

Page 37: GraphQL - APIs mais robustas e flexíveis

query { me { __typename ... on Admin { name } ... on User { name age } }}

Query: Múltiplos tiposSintaxe

{ "data": { "me": { "__typename": "User", "name": "Bruno Lemos", "age": 23 } }}

A query ‘me’ pode retornar um tipo diferentedependendo de quem está logado no momento

Page 38: GraphQL - APIs mais robustas e flexíveis

Ok, chega de query

Se as queries são como o GET do REST, como fazer o POST / PUT / DELETE?

Page 39: GraphQL - APIs mais robustas e flexíveis

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)

Page 40: GraphQL - APIs mais robustas e flexíveis

MutationSintaxe

mutation { addUser(name: “Bruno Lemos”) { _id name }}

{ "data": { "addUser": { "_id": "xxx_2", "name": "Bruno Lemos" } }}

Page 41: GraphQL - APIs mais robustas e flexíveis

MutationSintaxe

mutation { deleteUser(_id: “xxx”)}

{ "data": { "deleteUser": true }}

Page 42: GraphQL - APIs mais robustas e flexíveis

MutationSintaxe

mutation { deleteUser(_id: “id_nao_existente”)}

{ "data": { ? }}

Page 43: GraphQL - APIs mais robustas e flexíveis

MutationSintaxe

mutation { deleteUser(_id: “id_nao_existente”)}

{ "data": { "deleteUser": null }, "errors": [ { "message": "Usuário não encontrado.", "path": [ "deleteUser" ], } ]}

Page 44: GraphQL - APIs mais robustas e flexíveis

VariáveisSintaxe

mutation($name: String!) { addUser(name: $name) { _id name }}

//QUERY VARIABLES{ "name": "Bruno Lemos"}

{ "data": { "addUser": { "_id": "xxx_3", "name": "Bruno Lemos" } }}

Page 45: GraphQL - APIs mais robustas e flexíveis

FragmentSintaxe

{ "data": { "user": { "_id": "xxx", "name": "Bruno Lemos", "github": "brunolemos" } }}

query { user(_id: “xxx”) { _id ...RetornoPadrao }}

fragment RetornoPadrao on User { name github}

Page 46: GraphQL - APIs mais robustas e flexíveis

Na práticaAdicionando GraphQL à uma API já existente

Page 47: GraphQL - APIs mais robustas e flexíveis

Vamos criar uma query que receba um argumento _id e retorne o usuário correspondente.

Na prática

Page 48: GraphQL - APIs mais robustas e flexíveis

// Vamos usar Node.js

// Dependências:

$ npm i -S express graphql express-graphql

Antes de tudo...Servidor

Page 49: GraphQL - APIs mais robustas e flexíveis

index.jsconst 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 }));

Page 50: GraphQL - APIs mais robustas e flexíveis

./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 queryTipos já existentes: ID, String, Int, Boolean, Object, Enum, List, … (ver lista completa)

Criando o servidor

Page 51: GraphQL - APIs mais robustas e flexíveis

./user/query.js

Cada Query é um objeto comumDefine 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 promiseconst 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};

Page 52: GraphQL - APIs mais robustas e flexíveis

schema.jsexport 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

Page 53: GraphQL - APIs mais robustas e flexíveis

Autenticação

Page 54: GraphQL - APIs mais robustas e flexíveis

Autenticação

query { login(email: “[email protected]”, password: “123”) { token _id name }}

{ "data": { "login": { "token": "ABCDEF", "_id": "xxx", "name": "Bruno Lemos" } }}

Autenticação

Page 55: GraphQL - APIs mais robustas e flexíveis

Autenticação

query { viewer(token: “ABCDEF”) { me { _id name } }}

{ "data": { "viewer": { "me": { "_id": "xxx", "name": "Bruno Lemos" } } }}

Autenticação

Page 56: GraphQL - APIs mais robustas e flexíveis

Autenticação

mutation { viewer(token: “ABCDEF”) { deleteUser(_id: “xxx”) }}

{ "data": { "viewer": { "deleteUser": true } }}

Autenticação

Page 57: GraphQL - APIs mais robustas e flexíveis

Autenticação

mutation { viewer(token: “ABCDEF”) { deleteUser(_id: “id_de_outro_usuario”) }}

{ "data": { "viewer": { "deleteUser": null } }, "errors": [ { "message": "Não autorizado.", "path": [ "deleteUser" ], } ]}

Autenticação

Page 58: GraphQL - APIs mais robustas e flexíveis

./viewer/query.jsexport default { ViewerRootQuery, // todas as queries que estarão dentro da query viewer args: { token: { type: GraphQLString }, }, resolve: (root, { token }, context) => { // context é global, acessível de todas as queries context.token = token;

try { context.user = jwt.verify(token, config.jwtSecret) || {}; } catch (err) { context.user = {}; }

return {}; },};

Autenticação

Page 59: GraphQL - APIs mais robustas e flexíveis

GraphiQL

Page 60: GraphQL - APIs mais robustas e flexíveis

GraphiQL

http://localhost:3000/graphql

Page 61: GraphQL - APIs mais robustas e flexíveis

http://localhost:3000/graphql

GraphiQLDocumentação automática

Page 62: GraphQL - APIs mais robustas e flexíveis

GraphiQLDocumentação automática

http://localhost:3000/graphql

Page 63: GraphQL - APIs mais robustas e flexíveis

GraphiQL Documentação automática, execução de queries

http://localhost:3000/graphql

Page 64: GraphQL - APIs mais robustas e flexíveis

GraphiQL Documentação automática, execução de queries

http://localhost:3000/graphql

Page 65: GraphQL - APIs mais robustas e flexíveis

GraphiQL Documentação automática, execução de queries, autocomplete

http://localhost:3000/graphql

Page 66: GraphQL - APIs mais robustas e flexíveis

GraphiQL = Documentação automática, execução de queries, autocomplete, …

http://localhost:3000/graphql

Page 67: GraphQL - APIs mais robustas e flexíveis

GraphiQL = Documentação automática, execução de queries, autocomplete, … É como ter um Graph API Explorer próprio!

https://developers.facebook.com/tools/explorer

Page 68: GraphQL - APIs mais robustas e flexíveis

Client

Page 69: GraphQL - APIs mais robustas e flexíveis

No React, cada parte da aplicação é um Component

Cada Component sabe os dados que precisa

Como obter estes dados do GraphQL?

Client

Page 70: GraphQL - APIs mais robustas e flexíveis

Client: Relay

Page 71: GraphQL - APIs mais robustas e flexíveis

Client: Relay

Page 72: GraphQL - APIs mais robustas e flexíveis

Client: Relay

class UserProfile extends Component { render() { var { name, avatar } = this.props.user; return ( <div> <img src={avatar}/> <p>{name}</p> </div> ); }}

// continua...

Relay

Page 73: GraphQL - APIs mais robustas e flexíveis

Client: RelayRelay

UserProfile = Relay.createContainer(UserProfile, { fragments: { user: () => Relay.QL` fragment on User { name, avatar, } `, },});

Page 74: GraphQL - APIs mais robustas e flexíveis

Libs

Page 75: GraphQL - APIs mais robustas e flexíveis

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

Page 76: GraphQL - APIs mais robustas e flexíveis

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

Page 77: GraphQL - APIs mais robustas e flexíveis

Próximos passos

Page 78: GraphQL - APIs mais robustas e flexíveis

Não abordamos:

Cache / DataLoader

Client a fundo (Como fazer mutations, …)

Do GraphQL:

Subscriptions / realtime

Directives (@defer, @export, …)

Próximos passos

Page 79: GraphQL - APIs mais robustas e flexíveis

GraphQL é o futuro?

Page 80: GraphQL - APIs mais robustas e flexíveis

Obrigado@brunolemos