Share
Embed
No REST for the wicked: an introduction to GraphQL
GraphQL is a query language introduced by Facebook for building APIs which challenges one of the fundamental tenets of modern web development, aka REST. In this talk, we will go through the basics of GraphQL and see how it can help us build incredibly multifaceted APIs.
Ju Liu
January 14, 2016
Transcript
78 slides
-
Hello! 4 My name is Ju ! 4 I'm an
engineer at AlphaSights 4 We build ambitious apps in Ember (talk to me!) Ember London @arkham -
4 A Graph Query Language 4 Invented by Facebook in
2012, now made open 4 Powers most of the Facebook iOS and Android apps Ember London @arkham -
We have a users table like this one +----+------+---------------+-----+-----------+ |
id | name | twitter | age | country | +----+------+---------------+-----+-----------+ | 1 | Ju | arkh4m | 29 | Italy | | 2 | Will | willrax | 268 | Australia | | 3 | Matz | yukihiro_matz | 50 | Japan | +----+------+---------------+-----+-----------+ Ember London @arkham -
Get all users - Query string { users { id,
name } } Ember London @arkham -
Get all users - JSON response { "data": { "users":
[ { "id": "1", "name": "Ju" }, { "id": "2", "name": "Will" }, { "id": "3", "name": "Matz" } ] } } Ember London @arkham -
Get one user - Query string { user(id: 1) {
id, name } } Ember London @arkham -
Get one user - JSON response { "data": { "user":
{ "id": "1", "name": "Ju" } } } Ember London @arkham -
Create a user - Query string mutation { user: createUser(
name: "Tom", twitter: "tomdale", country: "USA" ) { id, name } } Ember London @arkham -
Create a user - JSON response { "data": { "user":
{ "id": "4", "name": "Tom" } } } Ember London @arkham -
Error - Query string mutation { user: createUser( twitter: "tomdale",
country: "USA" ) { id, name } } Ember London @arkham -
Error - JSON response { "errors": [ { "message": "Field
'createUser' argument 'name' of type 'String!' is required but not provided." } ] } Ember London @arkham -
Now imagine we have a company table like this one
+----+-------------+---------------+ | id | name | city | +----+-------------+---------------+ | 1 | AlphaSights | London | | 2 | Heroku | San Francisco | +----+-------------+---------------+ Ember London @arkham -
And we add a company_id to each user +----+------+---------------+-----+-----------+------------+ |
id | name | twitter | age | country | company_id | +----+------+---------------+-----+-----------+------------+ | 1 | Ju | arkham | 29 | Italy | 1 | | 2 | Will | willrax | 268 | Australia | 1 | | 3 | Matz | yukihiro_matz | 50 | Japan | 2 | +----+------+---------------+-----+-----------+------------+ Ember London @arkham -
Relationships - Query string { user(id: 1) { id, name,
company { name } } } Ember London @arkham -
Relationships - JSON response { "data": { "user": { "id":
"1", "name": "Ju", "company": { "name": "AlphaSights" } } } } Ember London @arkham -
Nested relationships - Query string { user(id: 1) { id,
name, company { name employees: users { name } } } } Ember London @arkham -
Nested relationships - JSON response { "data": { "user": {
"id": "1", "name": "Ju", "company": { "name": "AlphaSights", "employees": [ { "name": "Ju" }, { "name": "Will" } ] } } } } Ember London @arkham -
4 Hierarchical 4 Product-centric 4 Client-specifies queries 4 Backwards Compatible
4 Structured, Arbitrary Code 4 Application-Layer Protocol 4 Strongly-typed 4 Introspective Ember London @arkham -
GraphQL request { user(id: 1) { id, name } }
Ember London @arkham -
GraphQL response { "data": { "user": { "id": "1", "name":
"Ju" } } } Ember London @arkham -
4 Clients should not be forced to depend on methods
that they do not use. Ember London @arkham -
4 Clients should not be forced to depend on methods
that they do not use. 4 Many client specific interfaces are better than one general purpose interface. Ember London @arkham -
4 Clients should not be forced to depend on methods
that they do not use. 4 Many client specific interfaces are better than one general purpose interface. 4 The dependency of one class to another one should depend on the smallest possible interface. Ember London @arkham -
4 Clients should not be forced to depend on methods
that they do not use. 4 Many client specific interfaces are better than one general purpose interface. 4 The dependency of one class to another one should depend on the smallest possible interface. 4 Make fine grained interfaces that are client specific. Ember London @arkham -
POST /repos/ { "name": "Hello-World", "description": "This is your first
example", "homepage": "https://example.com", "private": false, "has_issues": true, "has_wiki": true, "has_downloads": true } Ember London @arkham -
REST response { "id": 1296269, "owner": { "login": "octocat", "id":
1, "avatar_url": "https://example.com/images/error/octocat_happy.gif", "gravatar_id": "", "url": "https://api.example.com/users/octocat", "html_url": "https://example.com/octocat", "followers_url": "https://api.example.com/users/octocat/followers", "following_url": "https://api.example.com/users/octocat/following{/other_user}", "gists_url": "https://api.example.com/users/octocat/gists{/gist_id}", "starred_url": "https://api.example.com/users/octocat/starred{/owner}{/repo}", "subscriptions_url": "https://api.example.com/users/octocat/subscriptions", "organizations_url": "https://api.example.com/users/octocat/orgs", "repos_url": "https://api.example.com/users/octocat/repos", "events_url": "https://api.example.com/users/octocat/events{/privacy}", "received_events_url": "https://api.example.com/users/octocat/received_events", "type": "User", "site_admin": false }, "name": "Hello-World", "full_name": "octocat/Hello-World", "description": "This your first repo!", "private": false, "fork": true, "url": "https://api.example.com/repos/octocat/Hello-World", "html_url": "https://example.com/octocat/Hello-World", "archive_url": "http://api.example.com/repos/octocat/Hello-World/{archive_format}{/ref}", "assignees_url": "http://api.example.com/repos/octocat/Hello-World/assignees{/user}", "blobs_url": "http://api.example.com/repos/octocat/Hello-World/git/blobs{/sha}", "branches_url": "http://api.example.com/repos/octocat/Hello-World/branches{/branch}", "clone_url": "https://example.com/octocat/Hello-World.git", "collaborators_url": "http://api.example.com/repos/octocat/Hello-World/collaborators{/collaborator}", "comments_url": "http://api.example.com/repos/octocat/Hello-World/comments{/number}", "commits_url": "http://api.example.com/repos/octocat/Hello-World/commits{/sha}", "compare_url": "http://api.example.com/repos/octocat/Hello-World/compare/{base}...{head}", "contents_url": "http://api.example.com/repos/octocat/Hello-World/contents/{+path}", "contributors_url": "http://api.example.com/repos/octocat/Hello-World/contributors", "downloads_url": "http://api.example.com/repos/octocat/Hello-World/downloads", "events_url": "http://api.example.com/repos/octocat/Hello-World/events", "forks_url": "http://api.example.com/repos/octocat/Hello-World/forks", "git_commits_url": "http://api.example.com/repos/octocat/Hello-World/git/commits{/sha}", "git_refs_url": "http://api.example.com/repos/octocat/Hello-World/git/refs{/sha}", "git_tags_url": "http://api.example.com/repos/octocat/Hello-World/git/tags{/sha}", "git_url": "git:example.com/octocat/Hello-World.git", "hooks_url": "http://api.example.com/repos/octocat/Hello-World/hooks", "issue_comment_url": "http://api.example.com/repos/octocat/Hello-World/issues/comments{/number}", "issue_events_url": "http://api.example.com/repos/octocat/Hello-World/issues/events{/number}", "issues_url": "http://api.example.com/repos/octocat/Hello-World/issues{/number}", "keys_url": "http://api.example.com/repos/octocat/Hello-World/keys{/key_id}", "labels_url": "http://api.example.com/repos/octocat/Hello-World/labels{/name}", "languages_url": "http://api.example.com/repos/octocat/Hello-World/languages", "merges_url": "http://api.example.com/repos/octocat/Hello-World/merges", "milestones_url": "http://api.example.com/repos/octocat/Hello-World/milestones{/number}", "mirror_url": "git:git.example.com/octocat/Hello-World", "notifications_url": "http://api.example.com/repos/octocat/Hello-World/notifications{?since, all, participating}", "pulls_url": "http://api.example.com/repos/octocat/Hello-World/pulls{/number}", "releases_url": "http://api.example.com/repos/octocat/Hello-World/releases{/id}", "ssh_url": "[email protected]:octocat/Hello-World.git", "stargazers_url": "http://api.example.com/repos/octocat/Hello-World/stargazers", "statuses_url": "http://api.example.com/repos/octocat/Hello-World/statuses/{sha}", "subscribers_url": "http://api.example.com/repos/octocat/Hello-World/subscribers", "subscription_url": "http://api.example.com/repos/octocat/Hello-World/subscription", "svn_url": "https://svn.example.com/octocat/Hello-World", "tags_url": "http://api.example.com/repos/octocat/Hello-World/tags", "teams_url": "http://api.example.com/repos/octocat/Hello-World/teams", "trees_url": "http://api.example.com/repos/octocat/Hello-World/git/trees{/sha}", "homepage": "https://example.com", "language": null, "forks_count": 9, "stargazers_count": 80, "watchers_count": 80, "size": 108, "default_branch": "master", "open_issues_count": 0, "has_issues": true, "has_wiki": true, "has_pages": false, "has_downloads": true, "pushed_at": "2011-01-26T19:06:43Z", "created_at": "2011-01-26T19:01:12Z", "updated_at": "2011-01-26T19:14:43Z", "permissions": { "admin": false, "push": false, "pull": true } } Ember London @arkham -
mutation { repo: createRepo( name: "Hello-World", description: "This is your
first example", homepage: "https://example.com", private: false, has_issues: true, has_wiki: true, has_downloads: true ) { id name clone_url user: owner { avatar_url } } } Ember London @arkham -
{ "data": { repo: { "id": 1296269, "name": "Hello-World", "clone_url":
"https://example.com/octocat/Hello-World.git", "user": { "avatar_url": "https://example.com/images/error/octocat_happy.gif", } } } } Ember London @arkham -
Imagine you have a /articles endpoint. In the frontpage you
want to display related articles. Ember London @arkham -
Imagine you have a /articles endpoint. In the frontpage you
want to display related articles. Easy, right? Ember London @arkham -
Imagine you have a /articles endpoint. In the frontpage you
want to display related articles. Easy, right? So we just add them to the payload. Ember London @arkham -
Imagine you have a /articles endpoint. In the frontpage you
want to display related articles. Easy, right? So we just add them to the payload. Now every time you call the /articles payload you have to pay the performance price for something you don't really care about. Ember London @arkham -
{ articles { id title excerpt preview_image_url author { name
} relatedArticles(limit: 5) { title preview_image_url } } } Ember London @arkham -
Hmm, okay.. But can I use it in Ember? Ember
London @arkham -
Features 4 Automatic queries generation 4 Field and object aliasing
4 Async relationships 4 BelongsTo relationships 4 HasMany relationships (sort of) Ember London @arkham -
Adapter import { Adapter } from 'ember-graphql-adapter'; export default Adapter.extend({
endpoint: `${EmberENV.apiBaseUrl}/graph` }); Ember London @arkham -
Query query { companies { id name } } Ember
London @arkham -
Result { "data": { "companies": [ { id: "1", name:
"AlphaSights" }, { id: "2", name: "Heroku" } ] } } Ember London @arkham -
Articles 4 From REST to GraphQL 4 Initial Impressions on
GraphQL & Relay 4 GraphQL Overview - Getting Started with GraphQL and Node Ember London @arkham -
Videos 4 React.js Conf 2015 4 Exploring GraphQL 4 GraphQL
at The Financial Times Ember London @arkham -
Thanks! Questions? Slides @ bit.ly/ember-graph 4 Ju Liu @arkham 4
engineering.alphasights.com 4 github.com/alphasights/ember-graphql-adapter Ember London @arkham