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. No REST for the Wicked
    Ember London @arkham

    View full-size slide

  2. A gentle introduction to GraphQL
    Ember London @arkham

    View full-size slide

  3. 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

    View full-size slide

  4. What's this GraphQL thing?
    Ember London @arkham

    View full-size slide

  5. 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

    View full-size slide

  6. GraphQL*
    * Does not actually require a Graph
    Ember London @arkham

    View full-size slide

  7. A simple example
    Ember London @arkham

    View full-size slide

  8. 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

    View full-size slide

  9. Get all users - Query string
    {
    users {
    id,
    name
    }
    }
    Ember London @arkham

    View full-size slide

  10. Get all users - JSON response
    {
    "data": {
    "users": [
    { "id": "1", "name": "Ju" },
    { "id": "2", "name": "Will" },
    { "id": "3", "name": "Matz" }
    ]
    }
    }
    Ember London @arkham

    View full-size slide

  11. Get one user - Query string
    {
    user(id: 1) {
    id,
    name
    }
    }
    Ember London @arkham

    View full-size slide

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

    View full-size slide

  13. Create a user - Query string
    mutation {
    user: createUser(
    name: "Tom",
    twitter: "tomdale",
    country: "USA"
    ) {
    id,
    name
    }
    }
    Ember London @arkham

    View full-size slide

  14. Create a user - JSON response
    {
    "data": {
    "user": {
    "id": "4",
    "name": "Tom"
    }
    }
    }
    Ember London @arkham

    View full-size slide

  15. Error - Query string
    mutation {
    user: createUser(
    twitter: "tomdale",
    country: "USA"
    ) {
    id,
    name
    }
    }
    Ember London @arkham

    View full-size slide

  16. Error - JSON response
    {
    "errors": [
    {
    "message": "Field 'createUser' argument 'name' of
    type 'String!' is required but not provided."
    }
    ]
    }
    Ember London @arkham

    View full-size slide

  17. Relationships
    Relationships
    Relationships
    Ember London @arkham

    View full-size slide

  18. Now imagine we have a company table like this one
    +----+-------------+---------------+
    | id | name | city |
    +----+-------------+---------------+
    | 1 | AlphaSights | London |
    | 2 | Heroku | San Francisco |
    +----+-------------+---------------+
    Ember London @arkham

    View full-size slide

  19. 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

    View full-size slide

  20. Relationships - Query string
    {
    user(id: 1) {
    id,
    name,
    company {
    name
    }
    }
    }
    Ember London @arkham

    View full-size slide

  21. Relationships - JSON response
    {
    "data": {
    "user": {
    "id": "1",
    "name": "Ju",
    "company": {
    "name": "AlphaSights"
    }
    }
    }
    }
    Ember London @arkham

    View full-size slide

  22. Nested relationships - Query string
    {
    user(id: 1) {
    id,
    name,
    company {
    name
    employees: users {
    name
    }
    }
    }
    }
    Ember London @arkham

    View full-size slide

  23. Nested relationships - JSON response
    {
    "data": {
    "user": {
    "id": "1",
    "name": "Ju",
    "company": {
    "name": "AlphaSights",
    "employees": [
    { "name": "Ju" },
    { "name": "Will" }
    ]
    }
    }
    }
    }
    Ember London @arkham

    View full-size slide

  24. But why should I use it?
    Ember London @arkham

    View full-size slide

  25. 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

    View full-size slide

  26. Ember London @arkham

    View full-size slide

  27. No REST for the wicked!
    Ember London @arkham

    View full-size slide

  28. REST request
    GET /users/1
    Ember London @arkham

    View full-size slide

  29. REST response
    ???
    Ember London @arkham

    View full-size slide

  30. GraphQL request
    {
    user(id: 1) {
    id,
    name
    }
    }
    Ember London @arkham

    View full-size slide

  31. GraphQL response
    {
    "data": {
    "user": {
    "id": "1",
    "name": "Ju"
    }
    }
    }
    Ember London @arkham

    View full-size slide

  32. What You See Is What You Get
    Ember London @arkham

    View full-size slide

  33. Every GraphQL query is a contract
    Ember London @arkham

    View full-size slide

  34. Every GraphQL query is
    a frontend-driven contract
    Ember London @arkham

    View full-size slide

  35. SOLID
    Ember London @arkham

    View full-size slide

  36. Interface Segregation Principle
    Ember London @arkham

    View full-size slide

  37. 4 Clients should not be forced to depend on methods
    that they do not use.
    Ember London @arkham

    View full-size slide

  38. 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

    View full-size slide

  39. 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

    View full-size slide

  40. 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

    View full-size slide

  41. REST request
    Ember London @arkham

    View full-size slide

  42. 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

    View full-size slide

  43. 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

    View full-size slide

  44. Ember London @arkham

    View full-size slide

  45. GraphQL request
    Ember London @arkham

    View full-size slide

  46. 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

    View full-size slide

  47. GraphQL response
    Ember London @arkham

    View full-size slide

  48. {
    "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

    View full-size slide

  49. GraphQL ! ISP
    Ember London @arkham

    View full-size slide

  50. GraphQL queries allow to create
    infinite client specific interfaces
    Ember London @arkham

    View full-size slide

  51. But I don't care about ISP!!!
    Ember London @arkham

    View full-size slide

  52. ISP has feelings too...
    Ember London @arkham

    View full-size slide

  53. Imagine you have a /articles endpoint.
    Ember London @arkham

    View full-size slide

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

    View full-size slide

  55. Imagine you have a /articles endpoint.
    In the frontpage you want to display related articles.
    Easy, right?
    Ember London @arkham

    View full-size slide

  56. 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

    View full-size slide

  57. 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

    View full-size slide

  58. But what if you could do this?
    Ember London @arkham

    View full-size slide

  59. {
    articles {
    id
    title
    excerpt
    preview_image_url
    author {
    name
    }
    relatedArticles(limit: 5) {
    title
    preview_image_url
    }
    }
    }
    Ember London @arkham

    View full-size slide

  60. Ember London @arkham

    View full-size slide

  61. Hmm, okay.. But can I use it in Ember?
    Ember London @arkham

    View full-size slide

  62. YES
    github.com/alphasights/ember-graphql-adapter
    Ember London @arkham

    View full-size slide

  63. Features
    4 Automatic queries generation
    4 Field and object aliasing
    4 Async relationships
    4 BelongsTo relationships
    4 HasMany relationships (sort of)
    Ember London @arkham

    View full-size slide

  64. Adapter
    import { Adapter } from 'ember-graphql-adapter';
    export default Adapter.extend({
    endpoint: `${EmberENV.apiBaseUrl}/graph`
    });
    Ember London @arkham

    View full-size slide

  65. Serializer
    import { Serializer } from 'ember-graphql-adapter';
    export default Serializer.extend({});
    Ember London @arkham

    View full-size slide

  66. Model
    DS.Model.extend({
    name: DS.attr('string');
    });
    Ember London @arkham

    View full-size slide

  67. Route
    model: function() {
    return this.store.findAll('company')
    }
    Ember London @arkham

    View full-size slide

  68. Query
    query {
    companies {
    id
    name
    }
    }
    Ember London @arkham

    View full-size slide

  69. Result
    {
    "data": {
    "companies": [
    { id: "1", name: "AlphaSights" },
    { id: "2", name: "Heroku" }
    ]
    }
    }
    Ember London @arkham

    View full-size slide

  70. Ember London @arkham

    View full-size slide

  71. Ember Data is amazing
    Ember London @arkham

    View full-size slide

  72. You can just replace one resource
    Ember London @arkham

    View full-size slide

  73. You have no excuses, go and try GraphQL!
    Ember London @arkham

    View full-size slide

  74. Backend
    Node
    github.com/graphql/graphql-js
    Rails
    github.com/rmosolgo/graphql-ruby
    Ember London @arkham

    View full-size slide

  75. References
    4 learngraphql.com
    4 facebook.github.io/graphql/
    4 HN thread
    Ember London @arkham

    View full-size slide

  76. Articles
    4 From REST to GraphQL
    4 Initial Impressions on GraphQL & Relay
    4 GraphQL Overview - Getting Started with GraphQL
    and Node
    Ember London @arkham

    View full-size slide

  77. Videos
    4 React.js Conf 2015
    4 Exploring GraphQL
    4 GraphQL at The Financial Times
    Ember London @arkham

    View full-size slide

  78. 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

    View full-size slide