The Prehistory of GraphQL

The Prehistory of GraphQL

GraphQL Finland 2018 closing keynote

14af31a1a6e7a91422b4b644fd04a0bc?s=128

Dan Schafer

October 19, 2018
Tweet

Transcript

  1. Dan Schafer @dlschafer Mysterious Closing Keynote

  2. The Prehistory of GraphQL Dan Schafer @dlschafer

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

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

    Feb ’12 Today Prototype re
  5. Prehistory of GraphQL Aug ’12 Feb ’12 Prototype Feb ’08

    Creation of 2012-era FB Server Architecture
  6. ⚠ This isn’t the “one true way”

  7. ✅ This was Facebook in 2012

  8. # But GraphQL was built for this

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

  10. Why do we care?

  11. “How do I implement authorization?” “How do I make my

    server efficient?” “How do I cache my results?”
  12. None
  13. One Layer Deeper

  14. “How do I implement authorization?” “How do I make my

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

    server efficient?” “How do I cache my results?” GraphQL addresses
 none of these…
  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…
  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!
  18. Prehistory of GraphQL Aug ’12 Feb ’12 Prototype Feb ’08

    Creation of 2012-era FB Server Architecture
  19. 3 key architectural developments

  20. Data requirements form a tree Asynchronicity is mandatory Strive for

    single sources of truth
  21. Data requirements form a tree

  22. None
  23. I am not a designer

  24. None
  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
  26. I am not a SQL engineer

  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
  28. Sharding

  29. None
  30. None
  31. redis.log

  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}
  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
  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
  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
  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
  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
  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
  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
  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
  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 
 

  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
 
 

  43. Our data requirements formed a tree

  44. That tree describes
 our data fetching strategy

  45. How do we implement that?

  46. Asynchronicity is mandatory

  47. PHP5 primitives… are
 not asynchronous

  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);
  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()
  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()
  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();
  52. Yuck %

  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
  54. None
  55. https://www.infoq.com/presentations/Evolution-of-Code-Design-at-Facebook

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

  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
  58. https://gu.illau.me/posts/polyfilling-generators/

  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
  60. Double Yuck % %

  61. We spent so much time iterating on async primitives

  62. You don’t have to

  63. async/await

  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);
  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)) );
  66. Design your server abstractions around async primitives

  67. GraphQL did!

  68. The Book of GraphQL Chapter 6 Verse 4.2

  69. None
  70. None
  71. Asynchronicity is mandatory

  72. Strive for single sources of truth

  73. “How do I implement authorization?” “How do I make my

    server efficient?” “How do I cache my results?”
  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
  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!
  76. Objects

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

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

  79. Smart data objects

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

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

  82. class SmartUser { static gen( viewer: Viewer, id: string ):

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

    Promise<?SmartUser> {} getID(): string {} getName(): string {} genBestFriend(): Promise<?SmartUser> {} genProfilePhoto(): Promise<?SmartPhoto> {} }
  84. class SmartUser { static gen( viewer: Viewer, id: string ):

    Promise<?SmartUser> { 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); } }
  85. Single Source of Truth

  86. class SmartUser { static gen( viewer: Viewer, id: string ):

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

    Promise<?SmartUser> {} getID(): string {} getName(): string {} genBestFriend(): Promise<?SmartUser> {} genProfilePhoto(): Promise<?SmartPhoto> {} }
  88. class SmartUser { genBestFriend(): Promise<?SmartUser> { return SmartUser.gen(this.viewer, this.data.bestFriendId); }

    genProfilePhoto(): Promise<?SmartPhoto> { return SmartPhoto.gen(this.viewer, this.data.profilePhotoId); } }
  89. Avoid overfetching

  90. class SmartUser { static gen(viewer: Viewer, id: string): Promise<?SmartUser> genBestFriend():

    Promise<?SmartUser> genProfilePhoto(): Promise<?SmartPhoto> }
  91. function getBestFriendsProfilePhoto( viewer: Viewer, id: string ): Promise<?SmartPhoto> { 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<?SmartUser> genBestFriend(): Promise<?SmartUser> genProfilePhoto(): Promise<?SmartPhoto> }
  92. Self-reinforcing

  93. Preconditioning

  94. class SmartUser { static gen( viewer: Viewer, id: string ):

    Promise<?SmartUser> {} getID(): string {} getName(): string {} genBestFriend(): Promise<?SmartUser> {} genProfilePhoto(): Promise<?SmartPhoto> {} }
  95. These seem familiar…

  96. class SmartUser { static gen( viewer: Viewer, id: string ):

    Promise<?SmartUser> {} getID(): string {} getName(): string {} genBestFriend(): Promise<?SmartUser> {} genProfilePhoto(): Promise<?SmartPhoto> {} }
  97. class SmartUser { static gen( viewer: Viewer, id: string ):

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

    Promise<?SmartUser> {} getID(): string {} getName(): string {} genBestFriend(): Promise<?SmartUser> {} genProfilePhoto(): Promise<?SmartPhoto> {} } GraphQL Fields!
  99. class SmartUser { static gen( viewer: Viewer, id: string ):

    Promise<?SmartUser> {} getID(): string {} getName(): string {} genBestFriend(): Promise<?SmartUser> {} genProfilePhoto(): Promise<?SmartPhoto> {} } GraphQL Root Field
 (a.k.a. a field on Query)
  100. Shouldn’t Resolvers contain 
 your business logic?

  101. Resolvers map to
 your business logic!

  102. Thin API layer

  103. The Prehistory of GraphQL

  104. The Future of GraphQL

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

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

    Feb ’12 Today Prototype
  107. Future of GraphQL July ’15 Open Source Today

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

    2019
  109. None
  110. Future of GraphQL July ’15 Open Source Today

  111. What might we talk about?
 


  112. What might we talk about?
 
 What might we work

    on?
  113. Tooling Clients Servers

  114. Tooling Clients Servers ✅ ✅ ❓

  115. None
  116. GraphQL Implementions
 ✅

  117. GraphQL Implementions
 GraphQL-aware server stacks ✅ ❓

  118. GraphQL Native Servers

  119. We need
 Opinionated Server Frameworks

  120. Data requirements form a tree Asynchronicity is mandatory Strive for

    single sources of truth
  121. Thank you! Dan Schafer @dlschafer