Upgrade to Pro — share decks privately, control downloads, hide ads and more …

No REST for the wicked: an introduction to GraphQL

Ju Liu
January 14, 2016

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
Tweet

More Decks by Ju Liu

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. Get all users - Query string { users { id,

    name } } Ember London @arkham
  5. Get all users - JSON response { "data": { "users":

    [ { "id": "1", "name": "Ju" }, { "id": "2", "name": "Will" }, { "id": "3", "name": "Matz" } ] } } Ember London @arkham
  6. Get one user - Query string { user(id: 1) {

    id, name } } Ember London @arkham
  7. Get one user - JSON response { "data": { "user":

    { "id": "1", "name": "Ju" } } } Ember London @arkham
  8. Create a user - Query string mutation { user: createUser(

    name: "Tom", twitter: "tomdale", country: "USA" ) { id, name } } Ember London @arkham
  9. Create a user - JSON response { "data": { "user":

    { "id": "4", "name": "Tom" } } } Ember London @arkham
  10. Error - Query string mutation { user: createUser( twitter: "tomdale",

    country: "USA" ) { id, name } } Ember London @arkham
  11. Error - JSON response { "errors": [ { "message": "Field

    'createUser' argument 'name' of type 'String!' is required but not provided." } ] } Ember London @arkham
  12. Now imagine we have a company table like this one

    +----+-------------+---------------+ | id | name | city | +----+-------------+---------------+ | 1 | AlphaSights | London | | 2 | Heroku | San Francisco | +----+-------------+---------------+ Ember London @arkham
  13. 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
  14. Relationships - Query string { user(id: 1) { id, name,

    company { name } } } Ember London @arkham
  15. Relationships - JSON response { "data": { "user": { "id":

    "1", "name": "Ju", "company": { "name": "AlphaSights" } } } } Ember London @arkham
  16. Nested relationships - Query string { user(id: 1) { id,

    name, company { name employees: users { name } } } } Ember London @arkham
  17. Nested relationships - JSON response { "data": { "user": {

    "id": "1", "name": "Ju", "company": { "name": "AlphaSights", "employees": [ { "name": "Ju" }, { "name": "Will" } ] } } } } Ember London @arkham
  18. 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
  19. 4 Clients should not be forced to depend on methods

    that they do not use. Ember London @arkham
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. { "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
  27. Imagine you have a /articles endpoint. In the frontpage you

    want to display related articles. Ember London @arkham
  28. Imagine you have a /articles endpoint. In the frontpage you

    want to display related articles. Easy, right? Ember London @arkham
  29. 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
  30. 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
  31. { articles { id title excerpt preview_image_url author { name

    } relatedArticles(limit: 5) { title preview_image_url } } } Ember London @arkham
  32. Features 4 Automatic queries generation 4 Field and object aliasing

    4 Async relationships 4 BelongsTo relationships 4 HasMany relationships (sort of) Ember London @arkham
  33. Adapter import { Adapter } from 'ember-graphql-adapter'; export default Adapter.extend({

    endpoint: `${EmberENV.apiBaseUrl}/graph` }); Ember London @arkham
  34. Result { "data": { "companies": [ { id: "1", name:

    "AlphaSights" }, { id: "2", name: "Heroku" } ] } } Ember London @arkham
  35. Articles 4 From REST to GraphQL 4 Initial Impressions on

    GraphQL & Relay 4 GraphQL Overview - Getting Started with GraphQL and Node Ember London @arkham
  36. Videos 4 React.js Conf 2015 4 Exploring GraphQL 4 GraphQL

    at The Financial Times Ember London @arkham
  37. 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