$30 off During Our Annual Pro Sale. View Details »

The Prehistory of GraphQL

The Prehistory of GraphQL

GraphQL Finland 2018 closing keynote

Dan Schafer

October 19, 2018
Tweet

More Decks by Dan Schafer

Other Decks in Programming

Transcript

  1. Dan Schafer
    @dlschafer
    Mysterious Closing Keynote

    View Slide

  2. The Prehistory of GraphQL
    Dan Schafer
    @dlschafer

    View Slide

  3. History of GraphQL
    July ’15
    Aug ’12
    Evolution Open Source
    Feb ’12 Today
    Prototype

    View Slide

  4. History of GraphQL
    July ’15
    Aug ’12
    Evolution Open Source
    Feb ’12 Today
    Prototype
    re

    View Slide

  5. Prehistory of GraphQL
    Aug ’12
    Feb ’12
    Prototype
    Feb ’08
    Creation of 2012-era FB Server Architecture

    View Slide


  6. This isn’t the “one true way”

    View Slide


  7. This was Facebook in 2012

    View Slide

  8. #
    But GraphQL was built for this

    View Slide

  9. $
    …and it’s got some good ideas!

    View Slide

  10. Why do we care?

    View Slide

  11. “How do I implement authorization?”
    “How do I make my server efficient?”
    “How do I cache my results?”

    View Slide

  12. View Slide

  13. One Layer Deeper

    View Slide

  14. “How do I implement authorization?”
    “How do I make my server efficient?”
    “How do I cache my results?”

    View Slide

  15. “How do I implement authorization?”
    “How do I make my server efficient?”
    “How do I cache my results?”
    GraphQL addresses

    none of these…

    View Slide

  16. “How do I implement authorization?”
    “How do I make my server efficient?”
    “How do I cache my results?”
    …because these were already
    answered at FB in 2012…

    View Slide

  17. “How do I implement authorization?”
    “How do I make my server efficient?”
    “How do I cache my results?”
    …because these are concerns
    not specific to GraphQL!

    View Slide

  18. Prehistory of GraphQL
    Aug ’12
    Feb ’12
    Prototype
    Feb ’08
    Creation of 2012-era FB Server Architecture

    View Slide

  19. 3 key architectural
    developments

    View Slide

  20. Data requirements form a tree
    Asynchronicity is mandatory
    Strive for single sources of truth

    View Slide

  21. Data requirements form a tree

    View Slide

  22. View Slide

  23. I am not a designer

    View Slide

  24. View Slide

  25. SELECT
    `users.name`
    FROM `friends`
    WHERE `id1` = 1
    LEFT JOIN `users` as `u2`
    WHERE `users.bestId` = `u2.id`
    LEFT JOIN `friends`
    WHERE `users.id` = `friends.id1`
    LEFT JOIN `users` as `u3`
    WHERE `friends.id2 = u3.id`
    LEFT JOIN `users` as `u4`
    WHERE `u3.bestId` = `u4.id`
    query.sql

    View Slide

  26. I am not a SQL engineer

    View Slide

  27. SELECT
    `users.name`
    FROM `friends`
    WHERE `id1` = 1
    LEFT JOIN `users` as `u2`
    WHERE `users.bestId` = `u2.id`
    LEFT JOIN `friends`
    WHERE `users.id` = `friends.id1`
    LEFT JOIN `users` as `u3`
    WHERE `friends.id2 = u3.id`
    LEFT JOIN `users` as `u4`
    WHERE `u3.bestId` = `u4.id`
    query.sql

    View Slide

  28. Sharding

    View Slide

  29. View Slide

  30. View Slide

  31. redis.log

    View Slide

  32. GET cu
    GET u:2
    LRANGE fr:1 0 5
    GET u:3
    GET u:4
    GET u:5
    GET u:6
    GET u:7
    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12
    redis.log
    {i:1 n:Dan bf:2}
    {i:2 n:Mary}
    [3,4,5,6,7]
    {i:3 n:Nick bf:8}
    {i:4 n:Lee bf:9}
    {i:5 n:Alex bf:10}
    {i:6 n:Laney bf:11}
    {i:7 n:Ola bf:12}
    {i:8 n:Leslie}
    {i:9 n:Ash}
    {i:10 n:Nina}
    {i:11 n:Turner}
    {i:12 n:Katie}

    View Slide

  33. GET cu
    GET u:2
    LRANGE fr:1 0 5
    GET u:3
    GET u:4
    GET u:5
    GET u:6
    GET u:7
    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12

    View Slide

  34. Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    GET u:3
    GET u:4
    GET u:5
    GET u:6
    GET u:7
    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12

    View Slide

  35. Mary
    Friends
    Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    GET u:3
    GET u:4
    GET u:5
    GET u:6
    GET u:7
    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12

    View Slide

  36. Laney
    Alex
    Lee
    Nick Ola
    Mary
    Friends
    Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    GET u:3
    GET u:4
    GET u:5
    GET u:6
    GET u:7
    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12

    View Slide

  37. Turner
    Nina
    Ash
    Leslie Katie
    Laney
    Alex
    Lee
    Nick Ola
    Mary
    Friends
    Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    GET u:3
    GET u:4
    GET u:5
    GET u:6
    GET u:7
    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12

    View Slide

  38. Turner
    Nina
    Ash
    Leslie Katie
    Laney
    Alex
    Lee
    Nick Ola
    Mary
    Friends
    Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    GET u:3
    GET u:4
    GET u:5
    GET u:6
    GET u:7
    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12

    View Slide

  39. Turner
    Nina
    Ash
    Leslie Katie
    Laney
    Alex
    Lee
    Nick Ola
    Mary
    Friends
    Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    MGET u:3 u:4 u:5

    u:6 u:7



    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12

    View Slide

  40. Turner
    Nina
    Ash
    Leslie Katie
    Laney
    Alex
    Lee
    Nick Ola
    Mary
    Friends
    Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    MGET u:3 u:4 u:5

    u:6 u:7



    GET u:8
    GET u:9
    GET u:10
    GET u:11
    GET u:12

    View Slide

  41. Turner
    Nina
    Ash
    Leslie Katie
    Laney
    Alex
    Lee
    Nick Ola
    Mary
    Friends
    Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    MGET u:3 u:4 u:5

    u:6 u:7



    MGET u:8 u:9 u:10

    u:11 u:12


    View Slide

  42. Turner
    Nina
    Ash
    Leslie Katie
    Laney
    Alex
    Lee
    Nick Ola
    Mary
    Friends
    Dan
    GET cu
    GET u:2
    LRANGE fr:1 0 5
    MGET u:3 u:4 u:5

    u:6 u:7



    MGET u:8 u:9 u:10

    u:11 u:12



    View Slide

  43. Our data requirements
    formed a tree

    View Slide

  44. That tree describes

    our data fetching strategy

    View Slide

  45. How do we implement that?

    View Slide

  46. Asynchronicity is mandatory

    View Slide

  47. PHP5 primitives… are

    not asynchronous

    View Slide

  48. GET cu
    GET u:2
    LRANGE fr:1 0 5
    MGET u:3 u:4 u:5

    u:6 u:7
    MGET u:8 u:9 u:10

    u:11 u:12
    redis.log
    {i:1 n:Dan bf:2}
    {i:2 n:Mary}
    [3,4,5,6,7]
    {i:3 n:Nick bf:8}
    {i:4 n:Lee bf:9}
    {i:5 n:Alex bf:10}
    {i:6 n:Laney bf:11}
    {i:7 n:Ola bf:12}
    {i:8 n:Leslie}
    {i:9 n:Ash}
    {i:10 n:Nina}
    {i:11 n:Turner}
    {i:12 n:Katie}
    getCurrentUser();
    get(2);
    getFriends(1);
    get(3); get(4);
    get(5); get(6);
    get(7);
    get(8); get(9);
    get(10); get(11);
    get(12);

    View Slide

  49. GET cu
    GET u:2
    LRANGE fr:1 0 5
    MGET u:3 u:4 u:5

    u:6 u:7
    MGET u:8 u:9 u:10

    u:11 u:12
    redis.log
    {i:1 n:Dan bf:2}
    {i:2 n:Mary}
    [3,4,5,6,7]
    {i:3 n:Nick bf:8}
    {i:4 n:Lee bf:9}
    {i:5 n:Alex bf:10}
    {i:6 n:Laney bf:11}
    {i:7 n:Ola bf:12}
    {i:8 n:Leslie}
    {i:9 n:Ash}
    {i:10 n:Nina}
    {i:11 n:Turner}
    {i:12 n:Katie}
    getCurrentUser();
    DISPATCH()
    get(2);
    getFriends(1);
    DISPATCH()
    get(3); get(4);
    get(5); get(6);
    get(7);
    DISPATCH()
    get(8); get(9);
    get(10); get(11);
    get(12);
    DISPATCH()

    View Slide

  50. getCurrentUser();
    DISPATCH()
    get(2);
    getFriends(1);
    DISPATCH()
    get(3); get(4);
    get(5); get(6);
    get(7);
    DISPATCH()
    get(8); get(9);
    get(10); get(11);
    get(12);
    DISPATCH()

    View Slide

  51. get_current_user(true, $user);
    memcache_dispatch();
    get_user(2, true, $user2);
    get_friends(1, true, $friends);
    memcache_dispatch();
    get_user(3, true, $user3); get_user(4, true, $user4);
    get_user(5, true, $user5); get_user(6, true, $user6);
    get_user(7, true, $user7);
    memcache_dispatch();
    get_user(8, true, $user8); get_user(9, true, $user9);
    get_user(10, true, $user10); get_user(11, true, $user11);
    get_user(12, true, $user12);
    memcache_dispatch();

    View Slide

  52. Yuck
    %

    View Slide

  53. class MyPreparable extends Preparable {
    $some_data = null;
    $other_data = null;
    public function prepare($pass) {
    switch ($pass) {
    case 0:
    fetch_some_data($this->some_data)
    return true;
    case 1:
    fetch_other_data($this->some_data, $this->other_data)
    return false;
    }
    public function getData() {
    return $this->other_data;
    }
    }
    https://www.quora.com/Facebook-Infrastructure-What-are-preparables-and-how-are-they-implemented

    View Slide

  54. View Slide

  55. https://www.infoq.com/presentations/Evolution-of-Code-Design-at-Facebook

    View Slide

  56. https://www.infoq.com/presentations/Evolution-of-Code-Design-at-Facebook

    View Slide

  57. class MyPreparable extends Preparable {
    $some_data = null;
    $other_data = null;
    public function prepare($pass) {
    switch ($pass) {
    case 0:
    fetch_some_data($this->some_data)
    return true;
    case 1:
    fetch_other_data($this->some_data, $this->other_data)
    return false;
    }
    public function getData() {
    return $this->other_data;
    }
    }
    https://www.quora.com/Facebook-Infrastructure-What-are-preparables-and-how-are-they-implemented

    View Slide

  58. https://gu.illau.me/posts/polyfilling-generators/

    View Slide

  59. class MyPreparable extends Preparable {
    $some_data = null;
    $other_data = null;
    public function prepare($pass) {
    switch ($pass) {
    case 0:
    fetch_some_data($this->some_data)
    return true;
    case 1:
    fetch_other_data($this->some_data, $this->other_data)
    return false;
    }
    public function getData() {
    return $this->other_data;
    }
    }
    https://www.quora.com/Facebook-Infrastructure-What-are-preparables-and-how-are-they-implemented

    View Slide

  60. Double Yuck
    % %

    View Slide

  61. We spent so much time
    iterating on async primitives

    View Slide

  62. You don’t have to

    View Slide

  63. async/await

    View Slide

  64. GET cu
    GET u:2
    LRANGE fr:1 0 5
    MGET u:3 u:4 u:5

    u:6 u:7
    MGET u:8 u:9 u:10

    u:11 u:12
    redis.log
    {i:1 n:Dan bf:2}
    {i:2 n:Mary}
    [3,4,5,6,7]
    {i:3 n:Nick bf:8}
    {i:4 n:Lee bf:9}
    {i:5 n:Alex bf:10}
    {i:6 n:Laney bf:11}
    {i:7 n:Ola bf:12}
    {i:8 n:Leslie}
    {i:9 n:Ash}
    {i:10 n:Nina}
    {i:11 n:Turner}
    {i:12 n:Katie}
    getCurrentUser();
    get(2);
    getFriends(1);
    get(3); get(4);
    get(5); get(6);
    get(7);
    get(8); get(9);
    get(10); get(11);
    get(12);

    View Slide

  65. GET cu
    GET u:2
    LRANGE fr:1 0 5
    MGET u:3 u:4 u:5

    u:6 u:7
    MGET u:8 u:9 u:10

    u:11 u:12
    redis.log
    {i:1 n:Dan bf:2}
    {i:2 n:Mary}
    [3,4,5,6,7]
    {i:3 n:Nick bf:8}
    {i:4 n:Lee bf:9}
    {i:5 n:Alex bf:10}
    {i:6 n:Laney bf:11}
    {i:7 n:Ola bf:12}
    {i:8 n:Leslie}
    {i:9 n:Ash}
    {i:10 n:Nina}
    {i:11 n:Turner}
    {i:12 n:Katie}
    await genCurrentUser();
    await Promise.all(
    gen(2), genFriends(1)
    );
    await Promise.all(
    [3,4,5,6,7].map(x => gen(x))
    );
    await Promise.all(
    [8,9,10,11,12].map(x => gen(x))
    );

    View Slide

  66. Design your server
    abstractions around async
    primitives

    View Slide

  67. GraphQL did!

    View Slide

  68. The Book of GraphQL
    Chapter 6
    Verse 4.2

    View Slide

  69. View Slide

  70. View Slide

  71. Asynchronicity is mandatory

    View Slide

  72. Strive for single sources of truth

    View Slide

  73. “How do I implement authorization?”
    “How do I make my server efficient?”
    “How do I cache my results?”

    View Slide

  74. “How do I implement authorization?”
    “How do I make my server efficient?”
    “How do I cache my results?”
    Always want to get

    these things right

    View Slide

  75. “How do I implement authorization?”
    “How do I make my server efficient?”
    “How do I cache my results?”
    Only want to write

    them once!

    View Slide

  76. Objects

    View Slide

  77. http://sujaytrivedi.blogspot.com/2015/03/object-oriented-programming-oop-in-c.html

    View Slide

  78. https://upload.wikimedia.org/wikipedia/commons/b/b8/Policy_Admin_Component_Diagram.PNG

    View Slide

  79. Smart data objects

    View Slide

  80. Smart data objects
    FB Jargon: Ent, short for “Entity”

    View Slide

  81. Smart data objects
    FB Jargon: Ent, short for “Entity”

    View Slide

  82. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }

    View Slide

  83. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }

    View Slide

  84. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {
    const data = await genUserFromDb(id);
    if (!data) { return null; }
    const canSee = await genCanViewerSeeUser(viewer, data);
    if (!canSee) { return null; }
    return new SmartUser(viewer, id, data);
    }
    }

    View Slide

  85. Single Source of Truth

    View Slide

  86. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }

    View Slide

  87. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }

    View Slide

  88. class SmartUser {
    genBestFriend(): Promise {
    return SmartUser.gen(this.viewer, this.data.bestFriendId);
    }
    genProfilePhoto(): Promise {
    return SmartPhoto.gen(this.viewer, this.data.profilePhotoId);
    }
    }

    View Slide

  89. Avoid overfetching

    View Slide

  90. class SmartUser {
    static gen(viewer: Viewer, id: string): Promise
    genBestFriend(): Promise
    genProfilePhoto(): Promise
    }

    View Slide

  91. function getBestFriendsProfilePhoto(
    viewer: Viewer, id: string
    ): Promise {
    const user = await SmartUser.gen(viewer, id);
    if (user == null) { return null; }
    const bestFriend = await user.genBestFriend();
    if (bestFriend == null) { return null; }
    return await bestFriend.genProfilePhoto();
    }
    class SmartUser {
    static gen(viewer: Viewer, id: string): Promise
    genBestFriend(): Promise
    genProfilePhoto(): Promise
    }

    View Slide

  92. Self-reinforcing

    View Slide

  93. Preconditioning

    View Slide

  94. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }

    View Slide

  95. These seem familiar…

    View Slide

  96. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }

    View Slide

  97. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }
    GraphQL Object Type!

    View Slide

  98. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }
    GraphQL Fields!

    View Slide

  99. class SmartUser {
    static gen(
    viewer: Viewer,
    id: string
    ): Promise {}
    getID(): string {}
    getName(): string {}
    genBestFriend(): Promise {}
    genProfilePhoto(): Promise {}
    }
    GraphQL Root Field

    (a.k.a. a field on Query)

    View Slide

  100. Shouldn’t Resolvers contain 

    your business logic?

    View Slide

  101. Resolvers map to

    your business logic!

    View Slide

  102. Thin API layer

    View Slide

  103. The Prehistory of GraphQL

    View Slide

  104. The Future of GraphQL

    View Slide

  105. History of GraphQL
    July ’15
    Aug ’12
    Evolution Open Source
    Feb ’12 Today
    Prototype

    View Slide

  106. History of GraphQL
    July ’15
    Aug ’12
    Evolution Open Source
    Feb ’12 Today
    Prototype

    View Slide

  107. Future of GraphQL
    July ’15
    Open Source
    Today

    View Slide

  108. Future of GraphQL
    July ’15
    Open Source
    Today June 20–21, 2019

    View Slide

  109. View Slide

  110. Future of GraphQL
    July ’15
    Open Source
    Today

    View Slide

  111. What might we talk about?


    View Slide

  112. What might we talk about?


    What might we work on?

    View Slide

  113. Tooling
    Clients
    Servers

    View Slide

  114. Tooling
    Clients
    Servers



    View Slide

  115. View Slide

  116. GraphQL Implementions


    View Slide

  117. GraphQL Implementions

    GraphQL-aware server stacks


    View Slide

  118. GraphQL Native Servers

    View Slide

  119. We need

    Opinionated Server
    Frameworks

    View Slide

  120. Data requirements form a tree
    Asynchronicity is mandatory
    Strive for single sources of truth

    View Slide

  121. Thank you!
    Dan Schafer
    @dlschafer

    View Slide