graphql, l'avenir du rest ?
TRANSCRIPT
G R A P H Q L , L’ AV E N I R D U R E S T ?
F R A N Ç O I S Z A N I N O T T O - @ f r a n c o i s z
G R A P H Q Lg r a f kɥɛ l
G R A S K A F K A E C U E L L E
L E P R O B L È M E
PA R T I E 1 / 6
R E S T
1. GET /user
2. GET /tweets
3. GET /users?ids=[123,456,789,…]
4. GET /tweet_stats?ids=[123,456,789,…]
5. GET /notifications
6. POST /views
B R E A K I N G C H A N G E
R E S T I N P E A C E
• Trop de requêtes par page
• Mauvaise performance sur mobile
• Pas de standard
• Pas de schema
• Evolution impossible
• Actions limitées au CRUD
L E S A U T R E S C A N D I D AT S
PA R T I E 2 / 6
R E S T + +
GET /tweets?include=author&fields=[id,date,body]
S Q L O V E R H T T P
GET /data?query=SELECT t.id, t.date,t.body,a.nameFROM tweets t LEFT JOIN author a ON tweet.author_id = author.id
LIMIT 10
P R O T O C O L B U F F E R S
FA L C O R
L E S A I N T G R A A L
• Langage de requêtage déclaratif
• Reposant sur du Remote Procedure Call
• Typage fort, schema
• Support des agrégats
• Standardisé
• Non lié à HTTP
D É C O U V R E Z G R A P H Q L
PA R T I E 3 / 6
POST / HTTP 1.1 Host: http://graphql.acme.com/ Content-Type: application/graphql { getTweet(id: 123) { id body date } }
HTTP/1.1 200 OK { "data": { "getTweet": { "id": "123", "body": "Lorem Ipsum", "date": "2017-07-14" } } }
GET /tweets/123 HTTP 1.1 Host: http://rest.acme.com/
HTTP/1.1 200 OK { "id": 123, "body": "Lorem Ipsum", "user_id": 456, "views": 45, "date": "2017-07-14", // etc. }
Req
uête
Rép
ons
e
REST GraphQL
POST / HTTP 1.1 Host: http://graphql.acme.com/ Content-Type: application/graphql { getTweets(limit: 10, sortField: "date", sortOrder: "DESC") { id body date } getUser { fullName } getNotificationsMeta { count } }
Req
uête
HTTP/1.1 200 OK Content-Type: application/json { "data": { "getTweets": [ { "id": "752", "body": "The guy next to me is listening...", "date": "2017-07-15T13:17:42.772Z", }, { "id": "123", "body": "The Espionnage Act was designed to...", "date": "2017-07-14T12:44:17.449Z" }, // etc. ], "getUser": { "fullName": "John Doe" }, "getNotificationsMeta": { "count": 12 } } }
Rép
ons
e
POST / HTTP 1.1 Host: http://graphql.acme.com/ Content-Type: application/graphql { getTweets(limit: 10, sortField: "date", sortOrder: "DESC") { id body date Author { username fullName avatarUrl } Stat { nbResponses nbRetweets nbLikes } } getUser { fullName } getNotificationsMeta { count } }
Req
uête
POST / HTTP 1.1 Host: http://graphql.acme.com/ Content-Type: application/graphql { getTweets(limit: 10, sortField: "date", sortOrder: "DESC") { id body date Author { username fullName avatarUrl } Stat { nbResponses nbRetweets nbLikes } } getUser { fullName } getNotificationsMeta { count } }
Req
uête
{ "data": { "getTweets": [ { "id": "752", "body": "The guy next to me is listening...", "date": "2017-07-15T13:17:42.772Z", "Author": { "username": "quantian1", "fullName": "Quantian", "avatarUrl": "https://amce.com/avatars/34345745634", }, "Stat": { "nbResponses": 3 "nbRetweets": 4, "nbLikes": 40 } }, // etc. ], "getUser": { "fullName": "John Doe" }, "getNotificationsMeta": { "count": 12 } } }
Rép
ons
e
POST / HTTP 1.1 Host: http://graphql.acme.com/ Content-Type: application/graphql { getTweets(limit: 10, sortField: "date", sortOrder: "DESC") { id body date Author { username fullName avatarUrl } Stat { nbResponses nbRetweets nbLikes } } getUser { fullName } getNotificationsMeta { count } }
Req
uête
# entry points type Query { getTweet(id: ID!): Tweet getTweets(limit: Int, sortField: String, sortOrder: String): [Tweet] getUser: User getNotificationsMeta: Meta }
# custom types type Tweet { id: ID! body: String date: Date Author: User Stats: Stat }
type User { id: ID! username: String firstName: String lastName: String fullName: String name: String @deprecated avatarUrl: Url
# entry points type Query { getTweet(id: ID!): Tweet getTweets(limit: Int, sortField: String, sortOrder: String): [Tweet] getUser: User getNotificationsMeta: Meta }
# custom types type Tweet { id: ID! body: String date: Date Author: User Stats: Stat }
type User { id: ID! username: String firstName: String lastName: String fullName: String name: String @deprecated avatarUrl: Url
POST / HTTP 1.1 Host: http://graphql.acme.com/ Content-Type: application/graphql { getTweets(limit: 10, sortField: "date", sortOrder: "DESC") { id body date(timezone: "UTC +2") Author { username fullName avatarUrl TopTweets(limit: 10) { body date(timezone: "UTC +2 ») Stats { nbResponses nbRetweets } } } Stat { nbResponses nbRetweets } } }
Req
uête
L E L A N G A G E G R A P H Q L
• Types
• Champs
• Requêtes, Mutations, Abonnements
• Variables
• Fragments
• Directives
C O M M E N T G R A P H Q L R É P O N D A V O S D E M A N D E S
PA R T I E 4 / 6
type Query { getTweet(id: ID!): Tweet getTweets(limit: Int): [Tweet] } type Tweet { id: ID! body: String Author: User } type User { fullName: String }
const resolvers = { Query: { getTweet: (_, params) => tweets.find(t => t.id == params.id), getTweets: (_, params) => tweets.slice(0, params.limit), }, Tweet: { id: tweet => tweet.id, body: tweet => tweet.body, Author: tweet => users.find(a => a == tweet.author_id), }, User: { fullName: user => `${user.first_name} ${user.last_name}`, }, };
{ getTweet(id: "1") { id body Author { fullName } } }
getTweet: (_, params) => tweets.find(t => t.id == params.id)
(null, { id: 1 }) { id: 1, body: 'Lorem Ipsum’, author_id: 10 }
1
{ getTweet(id: "1") { id body Author { fullName } } }
Tweet: { id: tweet => tweet.id, body: tweet => tweet.body, Author: tweet => users.find(a => a == tweet.author_id), }
{ id: 1, body: 'Lorem Ipsum’, author_id: 10 }12
{ getTweet(id: "1") { id body Author { fullName } } }
12 { id: ‘1’, body: 'Lorem Ipsum’, Author: {
id: 10, first_name: ‘John', last_name: 'Doe' } }
{ id: 1, body: 'Lorem Ipsum’, author_id: 10 }
{ getTweet(id: "1") { id body Author { fullName } } }
12
User: { fullName: user => `${user.first_name} ${user.last_name}`, }
3
{ id: 1, body: 'Lorem Ipsum’, author_id: 10 }
{ id: ‘1’, body: 'Lorem Ipsum’, Author: { id: 10, first_name: ‘John', last_name: 'Doe' } }
12
{ id: ‘1’, body: 'Lorem Ipsum’, Author: { fullName: ‘John Doe' } }
{ id: ‘1’, body: 'Lorem Ipsum’, Author: { id: 10, first_name: ‘John', last_name: 'Doe' } }
{ id: 1, body: 'Lorem Ipsum’, author_id: 10 }
3
{ getTweet(id: "1") { id body Author { fullName } } }
AVA I L A B L E I N Y O U R S E R V E R L A N G U A G E
• JavaScript
• PHP
• Ruby
• Python
• Go
• Java
• Scala
• C#
• Elixir
• Fortran
• BrainFuck
• etc…
const query = ` { getTweet(id: "1") { id body Author { name } } }`;
const headers = new Headers(); myHeaders.append('Content-Type', 'application/graphql');
fetch(‘/graphql’, { method: 'POST', body: query, headers }); .then(response => response.json) .then(response => response.data.getTweet) .then(tweet => ...);
import React from 'react'; import { gql, graphql } from 'react-apollo'; import LinearProgress from 'material-ui'; const Tweet = ({ data: { loading, tweet } }) => ( <div> {loading ? ( <LinearProgress /> ) : ( <div> <img src={tweet.author.avatar} className="avatar" /> <span className="author">{tweet.Author.name}</span> <div className="body">{tweet.body}</div> </div> )} </div> ); const query = gql`{ getTweet(id: "1") { id body Author { name } } }`; export default graphql(query)(App);
AVA I L A B L E I N Y O U R C L I E N T L A N G U A G E
• Vue.js
• React.js
• Angular.js
• Meteor.js
• Objective-C
• Swift
• Expo
• Java
• Kotlin
• Dart
• Fart
• etc…
T O U T E S T P R Ê T P O U R V O U S A C C U E I L L I R
• Simple
• Stable
• Sécurisé
• Performant
• Documenté
• Supporté
G R A P H Q L , A N G E O U D É M O N ?
PA R T I E 5 / 6
#diversité
POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 500 Internal Server Error POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 500 Internal Server Error POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK POST /graphql 200 OK
{ Tweets(limit: 100) { Author { Tweets(limit: 100) { Author { Tweets(limit: 100) { Author { Tweets(limit: 100) { Author { name } } } } } } } } }
Client Serverquery
Client Serverid
Dictionary Dictionary
query id id query
Pers
iste
d Q
uerie
s
G R A P H Q L , C ’ E S T P O U R Q U I ?
PA R T I E 6 / 6
Q U E L T Y P E D E D E V I C E U T I L I S E N T V O S C L I E N T S ?Q U E S T I O N # 1
D E S K T O P M O B I L E E T / O U D E S K T O P
L A P E R F O R M A N C E C Ô T É C L I E N T E S T- E L L E I M P O R TA N T E ?
Q U E S T I O N # 2
N O N
O U I
C O M B I E N D E R O U T E S A V O T R E A P I R E S T ? Q U E S T I O N # 3
5 A U P L U S
A U M O I S 6
Q U E L L E E S T L A D U R É E D E V I E D E V O T R E A P I ? Q U E S T I O N # 4
A U M O I N S U N A N Q U E L Q U E S
M O I S
Q U E L E S T L E N I V E A U D E C O M P L E X I T É D E V O T R E A P I ?
Q U E S T I O N # 5
S I M P L E
C O M P L E X E
S U R L A P L U S C O M P L E X E S D E S PA G E S D U C L I E N T, C O M B I E N D E P E R S I S T E N C E S S O N T A P P E L E E S ?
Q U E S T I O N # 6
U N
P L U S D E U N
AV E Z - V O U S D E S R È G L E S D E C A C H E C O M P L E X E S ? Q U E S T I O N # 7
N O N
O U I
C O M M E N T T R AVA I L L E N T V O S É Q U I P E S F R O N T E T B A C K ?
Q U E S T I O N # 8
B A C K L O G C O M M U N
S I L O
C O M B I E N D E D É V E L O P P E U R S ( F R O N T + B A C K ) T R AVA I L L E N T AV E C L’ A P I ?
Q U E S T I O N # 9
A U M O I N S C I N Q
U N À Q U AT R E
Q U E L E S T V O T R E T Y P E D E L A N G A G E P R É F É R É ?Q U E S T I O N # 1 0
F O R T E M E N T T Y P É
FA I B L E M E N T T Y P É
M E R C I ! D E S Q U E S T I O N S ?
F R A N Ç O I S Z A N I N O T T O @ f r a n c o i s z