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

How to write a GraphQL server

How to write a GraphQL server

What is GraphQL, how to write a GraphQL server, code examples, how to solve the N+1 problem, authentication and authorization.

Presentation: https://youtu.be/Tpf9kVE2AY8

Talk presented at Leiden Developers Meetup, 26 June 2017 and Xebia Knowledge Exchange, 27 June 2017

Avatar for Stefano Masini

Stefano Masini

June 26, 2017

Other Decks in Programming

Transcript

  1. History 1999 - REST 2005 - oData (M S) 2012

    - G raphQ L (Internal FB) Sep 2015 - Public release M ay 2017 - G raphQ L conf Use REST Find better Build Spread
  2. Entrypoints . . . Implementation A A http://domain.com/api/some/url_with_params?also=like_this Implementation B

    B http://domain.com/api/another/url_with_params Implementation C C http://domain.com/api/yet_another_url
  3. Limitations of REST • Too much/uninteresting data in the JSON

    • Proliferation of entrypoints • If strictly RESTful, too much network traffic • Fetch user, then fetch sites, then fetch projects...
  4. Graph...QL User 1 Site 1 Site 2 Site 3 Proj

    A Proj B Proj C Proj D user(id=1) { ... sites { ... projects { ... } } }
  5. Graph...QL User 1 Site 1 Site 2 Site 3 Proj

    A Proj B Proj C Proj D User 2 user(id=1) { ... sites { ... projects { ... users { ... } } } }
  6. Graph...QL User 1 Site 1 Site 2 Site 3 Proj

    A Proj B Proj C Proj D User 2 user(id=1) { ... sites { ... projects { ... users { ... sites { ... } } } } } Site 4 Site 5
  7. Resolvers User resolver Server JSON user(id=1) { ... sites {

    ... projects { ... users { ... } } } } Site resolver Site resolver Site resolver Proj resolver Proj resolver Proj resolver Proj resolver User resolver Complexity ~ O(n) n = size of JSON N+1 problem
  8. Sample exercise • Words Example: { text: "foo" } •

    Characters Example: { value: "f" } https://launchpad.graphql.com/j774pr55p launchpad.graphql.com
  9. GraphQL schema type Word { text: String letters: [Character] }

    Type definitions Resolvers const resolvers = { Word: { text: (word) => { return ...; }, letters: (word) => { return ...; } }, }; const WordType = new GraphQLObjectType({ name: 'Word', fields: () => ({ text: { type: GraphQLString, }, letters: { type: new GraphQLList(CharacterType), resolver: (word) => { return foo(); } }, }), }); + Executable schema
  10. N+1 problem User 1 Site 1 Site 2 Site 3

    SELECT * FROM sites WHERE user_id = 7 SELECT * FROM projects WHERE site_id = 1 SELECT * FROM projects WHERE site_id = 2 SELECT * FROM projects WHERE site_id = 3 Proj Proj Proj Proj Proj Proj Proj Proj Proj Proj Proj Proj 1 N
  11. 1 2 3 function complexCalculation(value) { 4 return value *

    2; 5 } 6 7 8 9 10 11 12 13 14 15 function f(param) { 16 17 return complexCalculation(param); 18 19 20 21 22 }
  12. 1 2 3 function complexCalculation(value) { 4 return value *

    2; 5 } 6 7 8 9 10 11 12 13 14 15 function f(param) { 16 17 return complexCalculation(param); 18 19 20 21 22 } 23 24 console.log(f(1)); 25 console.log(f(2));
  13. 1 2 3 function complexCalculation(value) { 4 return value *

    2; 5 } 6 7 8 9 10 11 12 13 14 15 function f(param) { 16 17 return complexCalculation(param); 18 19 20 21 22 } 23 24 console.log(f(1)); 25 console.log(f(2)); OUTPUT: 2 4
  14. 1 2 3 function complexCalculation(value) { 4 return value *

    2; 5 } 6 7 8 9 10 11 12 13 14 15 function f(param) { 16 return new Promise(resolve => { 17 resolve(complexCalculation(param)); 18 19 20 21 }); 22 } 23 24 f(1).then(val => console.log(val)); 25 f(2).then(val => console.log(val));
  15. 1 2 3 function complexCalculation(value) { 4 return value *

    2; 5 } 6 7 8 9 10 11 12 13 14 15 function f(param) { 16 return new Promise(resolve => { 17 resolve(complexCalculation(param)); 18 19 20 21 }); 22 } 23 24 f(1).then(val => console.log(val)); 25 f(2).then(val => console.log(val)); OUTPUT: 2 4
  16. 1 let collectedCalls = []; 2 3 function complexCalculation(value) {

    4 return value * 2; 5 } 6 7 function processAllPending() { 8 console.log(`${collectedCalls.length} values`); 9 for (let {param, resolve} of collectedCalls) { 10 resolve(complexCalculation(param)); 11 } 12 collectedCalls = []; 13 } 14 15 function f(param) { 16 return new Promise(resolve => { 17 collectedCalls.push({param, resolve}); 18 19 processAllPending(); 20 21 }); 22 } 23 24 f(1).then(val => console.log(val)); 25 f(2).then(val => console.log(val));
  17. 1 let collectedCalls = []; 2 3 function complexCalculation(value) {

    4 return value * 2; 5 } 6 7 function processAllPending() { 8 console.log(`${collectedCalls.length} values`); 9 for (let {param, resolve} of collectedCalls) { 10 resolve(complexCalculation(param)); 11 } 12 collectedCalls = []; 13 } 14 15 function f(param) { 16 return new Promise(resolve => { 17 collectedCalls.push({param, resolve}); 18 19 processAllPending(); 20 21 }); 22 } 23 24 f(1).then(val => console.log(val)); 25 f(2).then(val => console.log(val)); OUTPUT: 1 values 1 values 2 4
  18. 1 let collectedCalls = []; 2 3 function complexCalculation(value) {

    4 return value * 2; 5 } 6 7 function processAllPending() { 8 console.log(`${collectedCalls.length} values`); 9 for (let {param, resolve} of collectedCalls) { 10 resolve(complexCalculation(param)); 11 } 12 collectedCalls = []; 13 } 14 15 function f(param) { 16 return new Promise(resolve => { 17 collectedCalls.push({param, resolve}); 18 process.nextTick(() => { 19 processAllPending(); 20 }); 21 }); 22 } 23 24 f(1).then(val => console.log(val)); 25 f(2).then(val => console.log(val));
  19. 1 let collectedCalls = []; 2 3 function complexCalculation(value) {

    4 return value * 2; 5 } 6 7 function processAllPending() { 8 console.log(`${collectedCalls.length} values`); 9 for (let {param, resolve} of collectedCalls) { 10 resolve(complexCalculation(param)); 11 } 12 collectedCalls = []; 13 } 14 15 function f(param) { 16 return new Promise(resolve => { 17 collectedCalls.push({param, resolve}); 18 process.nextTick(() => { 19 processAllPending(); 20 }); 21 }); 22 } 23 24 f(1).then(val => console.log(val)); 25 f(2).then(val => console.log(val)); OUTPUT: 2 values 0 values 2 4
  20. Batching SELECT * FROM sites WHERE site_id = 3 SELECT

    * FROM sites WHERE site_id = 10 SELECT * FROM sites WHERE site_id = 24 SELECT * FROM sites WHERE site_id IN (3, 10, 24)
  21. N+1 problem (solved) User 1 SELECT * FROM sites WHERE

    user_id = 7 SELECT * FROM projects WHERE site_id IN (1, 2, 3) 1 1 Complexity (roughly speaking): O(log n) i.e. height of the JSON tree Site 1 Site 2 Site 3 Proj Proj Proj Proj Proj Proj Proj Proj Proj Proj
  22. Useful links • Apollo - http://dev.apollodata.com/ • Relay - https://facebook.github.io/relay/

    • Classic / Modern • Dataloader - https://www.npmjs.com/package/dataloader
  23. Authentication & authorization HTTP Request /graphql entrypoint controller Schema resolvers

    • Authentication • User profile • Global authorization • Schema choice • Context generation • User profile • DB connection pool • External services handles In each resolver: • Authorization • Execution Context