SlideShare uma empresa Scribd logo
1 de 80
GraphQL
APIs mais robustas e flexíveis
@brunolemos
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
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
1.Motivação
Imagine uma aplicação na qual você pode:
1. Adicionar amigos
2. Publicar posts
3. Curtir páginas
1. Motivação
Como você pegaria o último post de cada amigo seu?
1. Motivação
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.
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
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
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
👎👎👎👎👎👎
No REST, é fácil você se encontrar criando endpoints para retornos específicos.
Isto não é escalável.
1. Motivação
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
“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
GraphQL é criado pelo Facebook
Usado apenas internamente
2012
1. Motivação
GraphQL liberado para o público (open source)
2015
1. Motivação
Características
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
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
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”
Sintaxe
Query
Queries são como o GET do REST:
Você usa para obter dados, não podendo fazer mutações.
Query
Sintaxe
query {
user(_id: “xxx”) {
_id
name
}
}
{
"data": {
"user": {
"_id": "xxx",
"name": "Bruno Lemos"
}
}
}
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"
}
}
}
Query: Várias ao mesmo tempo
Sintaxe
query {
user(_id: “xxx”) {
_id
name
}
user(_id: “xxx”) {
github
}
}
{
"data": {
?
}
}
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"
},
}
}
Query: Várias ao mesmo tempo
Sintaxe
query {
user(_id: “xxx”) {
_id
name
}
user(_id: “xxx_2”) {
github
}
}
{
"data": {
?
}
}
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."
}]
}
Query: Alias
Sintaxe
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",
}
}
}
Query: Nested
Sintaxe
query {
user(_id: “xxx”) {
_id
name
friends(limit: 1) {
name
}
}
}
{
"data": {
"user": {
"_id": "xxx",
"name": "Bruno Lemos",
"friends": [{
"name": "Dan Abramov"
]}
}
}
}
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"
}],
}],
},
}
}
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
}
}
}
}
Query: Nested + Utils
Sintaxe
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
}
}
}
}
Query: Nested + Utils
Sintaxe
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"
}
}
}
}
Query: Nested + Utils
Sintaxe
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"
}
}
}
}
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
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
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
Ok, chega de query
Se as queries são como o GET do REST, como fazer o POST / PUT / DELETE?
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)
Mutation
Sintaxe
mutation {
addUser(name: “Bruno Lemos”) {
_id
name
}
}
{
"data": {
"addUser": {
"_id": "xxx_2",
"name": "Bruno Lemos"
}
}
}
Mutation
Sintaxe
mutation {
deleteUser(_id: “xxx”)
}
{
"data": {
"deleteUser": true
}
}
Mutation
Sintaxe
mutation {
deleteUser(_id: “id_nao_existente”)
}
{
"data": {
?
}
}
Mutation
Sintaxe
mutation {
deleteUser(_id: “id_nao_existente”)
}
{
"data": {
"deleteUser": null
},
"errors": [
{
"message": "Usuário não encontrado.",
"path": [
"deleteUser"
],
}
]
}
Variáveis
Sintaxe
mutation($name: String!) {
addUser(name: $name) {
_id
name
}
}
//QUERY VARIABLES
{
"name": "Bruno Lemos"
}
{
"data": {
"addUser": {
"_id": "xxx_3",
"name": "Bruno Lemos"
}
}
}
Fragment
Sintaxe
{
"data": {
"user": {
"_id": "xxx",
"name": "Bruno Lemos",
"github": "brunolemos"
}
}
}
query {
user(_id: “xxx”) {
_id
...RetornoPadrao
}
}
fragment RetornoPadrao on User {
name
github
}
Na prática
Adicionando GraphQL à uma API já existente
Vamos criar uma query que receba um argumento _id e retorne o usuário
correspondente.
Na prática
// Vamos usar Node.js
// Dependências:
$ npm i -S express graphql express-graphql
Antes de tudo...
Servidor
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 }));
./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
./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
};
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
Autenticação
Autenticação
query {
login(email: “x@gmail.com”, password: “123”) {
token
_id
name
}
}
{
"data": {
"login": {
"token": "ABCDEF",
"_id": "xxx",
"name": "Bruno Lemos"
}
}
}
Autenticação
Autenticação
query {
viewer(token: “ABCDEF”) {
me {
_id
name
}
}
}
{
"data": {
"viewer": {
"me": {
"_id": "xxx",
"name": "Bruno Lemos"
}
}
}
}
Autenticação
Autenticação
mutation {
viewer(token: “ABCDEF”) {
deleteUser(_id: “xxx”)
}
}
{
"data": {
"viewer": {
"deleteUser": true
}
}
}
Autenticação
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
./viewer/query.js
export 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
GraphiQL
GraphiQL
http://localhost:3000/graphql
http://localhost:3000/graphql
GraphiQL
Documentação automática
GraphiQL
Documentação automática
http://localhost:3000/graphql
GraphiQL
Documentação automática, execução de queries
http://localhost:3000/graphql
GraphiQL
Documentação automática, execução de queries
http://localhost:3000/graphql
GraphiQL
Documentação automática, execução de queries, autocomplete
http://localhost:3000/graphql
GraphiQL =
Documentação automática, execução de queries, autocomplete, …
http://localhost:3000/graphql
GraphiQL =
Documentação automática, execução de queries, autocomplete, … É como ter um Graph API Explorer próprio!
https://developers.facebook.com/tools/explorer
Client
No React, cada parte da aplicação é um Component
Cada Component sabe os dados que precisa
Como obter estes dados do GraphQL?
Client
Client: Relay
Client: Relay
Client: Relay
class UserProfile extends Component {
render() {
var { name, avatar } = this.props.user;
return (
<div>
<img src={avatar}/>
<p>{name}</p>
</div>
);
}
}
// continua...
Relay
Client: Relay
Relay
UserProfile = Relay.createContainer(UserProfile, {
fragments: {
user: () => Relay.QL`
fragment on User {
name,
avatar,
}
`,
},
});
Libs
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
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
Próximos passos
Não abordamos:
Cache / DataLoader
Client a fundo (Como fazer mutations, …)
Do GraphQL:
Subscriptions / realtime
Directives (@defer, @export, …)
Próximos passos
GraphQL é o futuro?
Obrigado
@brunolemos

Mais conteúdo relacionado

Destaque

Vim - Produtividade na ponta dos seus dedos
Vim - Produtividade na ponta dos seus dedosVim - Produtividade na ponta dos seus dedos
Vim - Produtividade na ponta dos seus dedosWilker Lucio
 
Apostila metrologia
Apostila metrologiaApostila metrologia
Apostila metrologialuizgraf
 
Relatorio redes estática
Relatorio redes estáticaRelatorio redes estática
Relatorio redes estáticaluizgraf
 
Relatorio redes dinamica
Relatorio redes dinamicaRelatorio redes dinamica
Relatorio redes dinamicaluizgraf
 
React Native in Production
React Native in ProductionReact Native in Production
React Native in ProductionSeokjun Kim
 
React Native Internals
React Native InternalsReact Native Internals
React Native InternalsTadeu Zagallo
 
SASS + COMPASS - Alta Produtividade no Front-end
SASS + COMPASS - Alta Produtividade no Front-endSASS + COMPASS - Alta Produtividade no Front-end
SASS + COMPASS - Alta Produtividade no Front-endAnderson Aguiar
 
GraphQL: Enabling a new generation of API developer tools
GraphQL: Enabling a new generation of API developer toolsGraphQL: Enabling a new generation of API developer tools
GraphQL: Enabling a new generation of API developer toolsSashko Stubailo
 
Apostila de PIC
Apostila de PICApostila de PIC
Apostila de PICluizgraf
 
Apostila de Pneumática
Apostila de PneumáticaApostila de Pneumática
Apostila de Pneumáticaluizgraf
 
Agile project tracking - burn up charts
Agile project tracking - burn up chartsAgile project tracking - burn up charts
Agile project tracking - burn up chartsJonny LeRoy
 
Apostila de análise de circuitos elétricos
Apostila de análise de circuitos elétricosApostila de análise de circuitos elétricos
Apostila de análise de circuitos elétricosluizgraf
 
Comunicação PC - PC via porta paralela
Comunicação PC - PC via porta paralelaComunicação PC - PC via porta paralela
Comunicação PC - PC via porta paralelaluizgraf
 
A tour of React Native
A tour of React NativeA tour of React Native
A tour of React NativeTadeu Zagallo
 
Aula de eletrônica digital
Aula de eletrônica digitalAula de eletrônica digital
Aula de eletrônica digitalluizgraf
 
Aulas de Eletrônica Analógica
Aulas de Eletrônica Analógica Aulas de Eletrônica Analógica
Aulas de Eletrônica Analógica luizgraf
 

Destaque (20)

Conhecendo API do Facebook
Conhecendo API do FacebookConhecendo API do Facebook
Conhecendo API do Facebook
 
Vim - Produtividade na ponta dos seus dedos
Vim - Produtividade na ponta dos seus dedosVim - Produtividade na ponta dos seus dedos
Vim - Produtividade na ponta dos seus dedos
 
Apostila metrologia
Apostila metrologiaApostila metrologia
Apostila metrologia
 
Meetup React Native
Meetup React NativeMeetup React Native
Meetup React Native
 
Relatorio redes estática
Relatorio redes estáticaRelatorio redes estática
Relatorio redes estática
 
Relatorio redes dinamica
Relatorio redes dinamicaRelatorio redes dinamica
Relatorio redes dinamica
 
React Native in Production
React Native in ProductionReact Native in Production
React Native in Production
 
React Native Internals
React Native InternalsReact Native Internals
React Native Internals
 
SASS + COMPASS - Alta Produtividade no Front-end
SASS + COMPASS - Alta Produtividade no Front-endSASS + COMPASS - Alta Produtividade no Front-end
SASS + COMPASS - Alta Produtividade no Front-end
 
GraphQL: Enabling a new generation of API developer tools
GraphQL: Enabling a new generation of API developer toolsGraphQL: Enabling a new generation of API developer tools
GraphQL: Enabling a new generation of API developer tools
 
Apostila de PIC
Apostila de PICApostila de PIC
Apostila de PIC
 
React & Redux
React & ReduxReact & Redux
React & Redux
 
React native - What, Why, How?
React native - What, Why, How?React native - What, Why, How?
React native - What, Why, How?
 
Apostila de Pneumática
Apostila de PneumáticaApostila de Pneumática
Apostila de Pneumática
 
Agile project tracking - burn up charts
Agile project tracking - burn up chartsAgile project tracking - burn up charts
Agile project tracking - burn up charts
 
Apostila de análise de circuitos elétricos
Apostila de análise de circuitos elétricosApostila de análise de circuitos elétricos
Apostila de análise de circuitos elétricos
 
Comunicação PC - PC via porta paralela
Comunicação PC - PC via porta paralelaComunicação PC - PC via porta paralela
Comunicação PC - PC via porta paralela
 
A tour of React Native
A tour of React NativeA tour of React Native
A tour of React Native
 
Aula de eletrônica digital
Aula de eletrônica digitalAula de eletrônica digital
Aula de eletrônica digital
 
Aulas de Eletrônica Analógica
Aulas de Eletrônica Analógica Aulas de Eletrônica Analógica
Aulas de Eletrônica Analógica
 

Semelhante a GraphQL - APIs mais robustas e flexíveis

MongoDB - Tudo o que você precisa saber
MongoDB - Tudo o que você precisa saberMongoDB - Tudo o que você precisa saber
MongoDB - Tudo o que você precisa saberChristiano Anderson
 
MongoDB - Tudo que você precisa saber - FGSL 2014
MongoDB - Tudo que você precisa saber - FGSL 2014MongoDB - Tudo que você precisa saber - FGSL 2014
MongoDB - Tudo que você precisa saber - FGSL 2014Christiano Anderson
 
MongoDB Schema Design - Latinoware 2014
MongoDB Schema Design - Latinoware 2014MongoDB Schema Design - Latinoware 2014
MongoDB Schema Design - Latinoware 2014Christiano Anderson
 
MongoDB - Tudo o que você precisa saber - FISL16
MongoDB - Tudo o que você precisa saber - FISL16MongoDB - Tudo o que você precisa saber - FISL16
MongoDB - Tudo o que você precisa saber - FISL16Christiano Anderson
 
Django e MongoDB - Python Brasil 7
Django e MongoDB - Python Brasil 7Django e MongoDB - Python Brasil 7
Django e MongoDB - Python Brasil 7Christiano Anderson
 
Desenvolvimento de Aplicações para o Google App Engine (CPBR5)
Desenvolvimento de Aplicações para o Google App Engine (CPBR5)Desenvolvimento de Aplicações para o Google App Engine (CPBR5)
Desenvolvimento de Aplicações para o Google App Engine (CPBR5)Carlos Duarte do Nascimento
 
Desenvolvimento de aplicações para o Google App Engine
Desenvolvimento de aplicações para o Google App EngineDesenvolvimento de aplicações para o Google App Engine
Desenvolvimento de aplicações para o Google App EngineCampus Party Brasil
 
Tornando as coisas mais simples com Azure Functions e Node.JS
Tornando as coisas mais simples com Azure Functions e Node.JSTornando as coisas mais simples com Azure Functions e Node.JS
Tornando as coisas mais simples com Azure Functions e Node.JSMatheus Donizete
 
Monitoramento de Aplicações Web Modernas com Zabbix
Monitoramento de Aplicações Web Modernas com ZabbixMonitoramento de Aplicações Web Modernas com Zabbix
Monitoramento de Aplicações Web Modernas com ZabbixAndré Déo
 
Construindo APIs RESTful com Spring
Construindo APIs RESTful com SpringConstruindo APIs RESTful com Spring
Construindo APIs RESTful com SpringMateus Malaquias
 
Fazendo uma aplicação cliente/servidor (e algumas dicas...)
Fazendo uma aplicação cliente/servidor (e algumas dicas...)Fazendo uma aplicação cliente/servidor (e algumas dicas...)
Fazendo uma aplicação cliente/servidor (e algumas dicas...)Makoto Miyagawa
 
Novidades do elasticsearch 2.0 e como usá-lo com PHP
Novidades do elasticsearch 2.0 e como usá-lo com PHPNovidades do elasticsearch 2.0 e como usá-lo com PHP
Novidades do elasticsearch 2.0 e como usá-lo com PHPLuiz Henrique Zambom Santana
 
Sendgrid Delivered São Paulo - Heitor
Sendgrid Delivered São Paulo - HeitorSendgrid Delivered São Paulo - Heitor
Sendgrid Delivered São Paulo - HeitorHeitor Tashiro Sergent
 
Desenvolvendo soluções com banco de dados não relacional - MongoDB
Desenvolvendo soluções com banco de dados não relacional - MongoDBDesenvolvendo soluções com banco de dados não relacional - MongoDB
Desenvolvendo soluções com banco de dados não relacional - MongoDBiMasters
 

Semelhante a GraphQL - APIs mais robustas e flexíveis (20)

MongoDB - Tudo o que você precisa saber
MongoDB - Tudo o que você precisa saberMongoDB - Tudo o que você precisa saber
MongoDB - Tudo o que você precisa saber
 
MongoDB - Tudo que você precisa saber - FGSL 2014
MongoDB - Tudo que você precisa saber - FGSL 2014MongoDB - Tudo que você precisa saber - FGSL 2014
MongoDB - Tudo que você precisa saber - FGSL 2014
 
MongoDB Schema Design - Latinoware 2014
MongoDB Schema Design - Latinoware 2014MongoDB Schema Design - Latinoware 2014
MongoDB Schema Design - Latinoware 2014
 
MongoDB - Tudo o que você precisa saber - FISL16
MongoDB - Tudo o que você precisa saber - FISL16MongoDB - Tudo o que você precisa saber - FISL16
MongoDB - Tudo o que você precisa saber - FISL16
 
TDC 2011 - Ext JS 4
TDC 2011 - Ext JS 4TDC 2011 - Ext JS 4
TDC 2011 - Ext JS 4
 
Meetup MUG-RS KingHost
Meetup MUG-RS KingHostMeetup MUG-RS KingHost
Meetup MUG-RS KingHost
 
Django e MongoDB - Python Brasil 7
Django e MongoDB - Python Brasil 7Django e MongoDB - Python Brasil 7
Django e MongoDB - Python Brasil 7
 
Python e MongoDB - Ensol
Python e MongoDB - EnsolPython e MongoDB - Ensol
Python e MongoDB - Ensol
 
Desenvolvimento de Aplicações para o Google App Engine (CPBR5)
Desenvolvimento de Aplicações para o Google App Engine (CPBR5)Desenvolvimento de Aplicações para o Google App Engine (CPBR5)
Desenvolvimento de Aplicações para o Google App Engine (CPBR5)
 
Desenvolvimento de aplicações para o Google App Engine
Desenvolvimento de aplicações para o Google App EngineDesenvolvimento de aplicações para o Google App Engine
Desenvolvimento de aplicações para o Google App Engine
 
Tornando as coisas mais simples com Azure Functions e Node.JS
Tornando as coisas mais simples com Azure Functions e Node.JSTornando as coisas mais simples com Azure Functions e Node.JS
Tornando as coisas mais simples com Azure Functions e Node.JS
 
Monitoramento de Aplicações Web Modernas com Zabbix
Monitoramento de Aplicações Web Modernas com ZabbixMonitoramento de Aplicações Web Modernas com Zabbix
Monitoramento de Aplicações Web Modernas com Zabbix
 
Construindo APIs RESTful com Spring
Construindo APIs RESTful com SpringConstruindo APIs RESTful com Spring
Construindo APIs RESTful com Spring
 
Treinamento Elasticsearch - Parte 1
Treinamento Elasticsearch - Parte 1Treinamento Elasticsearch - Parte 1
Treinamento Elasticsearch - Parte 1
 
MongoDB + PHP
MongoDB + PHPMongoDB + PHP
MongoDB + PHP
 
PHP GERAL
PHP GERALPHP GERAL
PHP GERAL
 
Fazendo uma aplicação cliente/servidor (e algumas dicas...)
Fazendo uma aplicação cliente/servidor (e algumas dicas...)Fazendo uma aplicação cliente/servidor (e algumas dicas...)
Fazendo uma aplicação cliente/servidor (e algumas dicas...)
 
Novidades do elasticsearch 2.0 e como usá-lo com PHP
Novidades do elasticsearch 2.0 e como usá-lo com PHPNovidades do elasticsearch 2.0 e como usá-lo com PHP
Novidades do elasticsearch 2.0 e como usá-lo com PHP
 
Sendgrid Delivered São Paulo - Heitor
Sendgrid Delivered São Paulo - HeitorSendgrid Delivered São Paulo - Heitor
Sendgrid Delivered São Paulo - Heitor
 
Desenvolvendo soluções com banco de dados não relacional - MongoDB
Desenvolvendo soluções com banco de dados não relacional - MongoDBDesenvolvendo soluções com banco de dados não relacional - MongoDB
Desenvolvendo soluções com banco de dados não relacional - MongoDB
 

GraphQL - APIs mais robustas e flexíveis

  • 1. GraphQL APIs mais robustas e flexíveis @brunolemos
  • 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
  • 5. Imagine uma aplicação na qual você pode: 1. Adicionar amigos 2. Publicar posts 3. Curtir páginas 1. Motivação
  • 6. Como você pegaria o último post de cada amigo seu? 1. Motivação
  • 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
  • 15. GraphQL liberado para o público (open source) 2015 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”
  • 21. Query Queries são como o GET do REST: Você usa para obter dados, não podendo fazer mutações.
  • 22. Query Sintaxe query { user(_id: “xxx”) { _id name } } { "data": { "user": { "_id": "xxx", "name": "Bruno Lemos" } } }
  • 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." }] }
  • 28. Query: Alias Sintaxe 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", } } }
  • 29. Query: Nested Sintaxe query { user(_id: “xxx”) { _id name friends(limit: 1) { name } } } { "data": { "user": { "_id": "xxx", "name": "Bruno Lemos", "friends": [{ "name": "Dan Abramov" ]} } } }
  • 30. 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" }], }], }, } }
  • 31. 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 } } } }
  • 32. Query: Nested + Utils Sintaxe 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 } } } }
  • 33. Query: Nested + Utils Sintaxe 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" } } } }
  • 34. Query: Nested + Utils Sintaxe 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" } } } }
  • 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)
  • 40. Mutation Sintaxe mutation { addUser(name: “Bruno Lemos”) { _id name } } { "data": { "addUser": { "_id": "xxx_2", "name": "Bruno Lemos" } } }
  • 43. Mutation Sintaxe mutation { deleteUser(_id: “id_nao_existente”) } { "data": { "deleteUser": null }, "errors": [ { "message": "Usuário não encontrado.", "path": [ "deleteUser" ], } ] }
  • 44. Variáveis Sintaxe mutation($name: String!) { addUser(name: $name) { _id name } } //QUERY VARIABLES { "name": "Bruno Lemos" } { "data": { "addUser": { "_id": "xxx_3", "name": "Bruno Lemos" } } }
  • 45. Fragment Sintaxe { "data": { "user": { "_id": "xxx", "name": "Bruno Lemos", "github": "brunolemos" } } } query { user(_id: “xxx”) { _id ...RetornoPadrao } } fragment RetornoPadrao on User { name github }
  • 46. Na prática Adicionando GraphQL à uma API já existente
  • 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
  • 54. Autenticação query { login(email: “x@gmail.com”, password: “123”) { token _id name } } { "data": { "login": { "token": "ABCDEF", "_id": "xxx", "name": "Bruno Lemos" } } } Autenticação
  • 55. Autenticação query { viewer(token: “ABCDEF”) { me { _id name } } } { "data": { "viewer": { "me": { "_id": "xxx", "name": "Bruno Lemos" } } } } Autenticação
  • 56. Autenticação mutation { viewer(token: “ABCDEF”) { deleteUser(_id: “xxx”) } } { "data": { "viewer": { "deleteUser": true } } } Autenticação
  • 57. 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
  • 58. ./viewer/query.js export 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
  • 63. GraphiQL Documentação automática, execução de queries http://localhost:3000/graphql
  • 64. GraphiQL Documentação automática, execução de queries http://localhost:3000/graphql
  • 65. GraphiQL Documentação automática, execução de queries, autocomplete http://localhost:3000/graphql
  • 66. GraphiQL = Documentação automática, execução de queries, autocomplete, … http://localhost:3000/graphql
  • 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
  • 69. No React, cada parte da aplicação é um Component Cada Component sabe os dados que precisa Como obter estes dados do GraphQL? Client
  • 72. Client: Relay class UserProfile extends Component { render() { var { name, avatar } = this.props.user; return ( <div> <img src={avatar}/> <p>{name}</p> </div> ); } } // continua... Relay
  • 73. Client: Relay Relay UserProfile = Relay.createContainer(UserProfile, { fragments: { user: () => Relay.QL` fragment on User { name, avatar, } `, }, });
  • 74. Libs
  • 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
  • 78. Não abordamos: Cache / DataLoader Client a fundo (Como fazer mutations, …) Do GraphQL: Subscriptions / realtime Directives (@defer, @export, …) Próximos passos
  • 79. GraphQL é o futuro?