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

Graph vs Graph

Rhys Evans
September 06, 2019

Graph vs Graph

Most stories of GraphQL implementations focus on retrofitting a GraphQL layer on top of existing APIs to surface the data's graphiness. But what if you're starting from scratch and you know you'll need a graph from day 1 - what role is there for GraphQL when it's not the only graph representation in your stack. I'll tell you how, at the FT, we've combined neo4j graph database with GraphQL to expose operational information about our business like never before.

Rhys Evans

September 06, 2019
Tweet

More Decks by Rhys Evans

Other Decks in Technology

Transcript

  1. Graph vs Graph GraphQL as the API for your graph

    database Rhys Evans, @wheresrhys
  2. If your data is a graph, and we’re all thinking

    in graphs, why is it that only one thin layer actually is a graph?
  3. Systems Teams System Team Join People Team Person Join System

    Person Join Budget lines Modules Data stores System Data Read Join System Module Join System Data Write Join
  4. All the Systems All the System joins All the Systems

    All the System joins All the Systems All the System joins ...
  5. No global lookup by ID Uses a local pointer to

    the exact physical location of the data
  6. CREATE CONSTRAINT ON (s:System) ASSERT s.code IS UNIQUE CREATE (s:System)

    SET s = $properties MERGE (s)-[r:DEPENDS_ON]-(s2) WHERE s2.code = “dependency” RETURN s, r, s2 Constructing the graph
  7. 1. Self-service 2. Everyone a power user 3. Low effort

    extensibility 4. API and UI for everything
  8. 1. Self-service 2. Everyone a power user 3. Low effort

    extensibility 4. API and UI for everything
  9. We need an API that can represent graphs and allows

    users to define the data they need
  10. ?

  11. @relation directive type System { code: String sla: SLA dependencies:

    [System] @relation(name: "DEPENDS_ON", direction: "OUT") dependents: [System] @relation(name: "DEPENDS_ON", direction: "IN") }
  12. import { v1 as neo4j } from 'neo4j-driver'; import {

    makeAugmentedSchema } from 'neo4j-graphql-js'; const driver = neo4j.driver(...); router.use('/graphql', graphqlExpress(() => ({ schema: makeAugmentedSchema({typeDefs}), context: { driver } }) ) Resolver generation
  13. N + 1 problem goes away N + 1 →

    1 N(M + 1) + 1 → 1 N(M(P + 1) + 1) + 1 → 1
  14. 1. Self-service 2. Everyone a power user 3. Low effort

    extensibility 4. API and UI for everything
  15. Say we want to add a new type and edge

    to the graph System Hosting Platform HOSTED_BY
  16. How do we add this to the API layer? System

    Hosting Platform HOSTED_BY
  17. type HostingPlatform { code: String hostsSystems: [System] @relation(name: "HOSTED_ON", direction:

    "IN") } extend type System { hostedOn: [HostingPlatform] @relation(name: "DEPENDS_ON", direction: "OUT") } Add it to the schema
  18. // function that returns GraphQL middleware const constructAPI = schema

    => { api = graphqlExpress(() => ({ schema: makeAugmentedSchema({schema}) }) } let api; constructAPI(initialSchema) schemaFilePoller.on('change', constructAPI); app.post('/graphql', (...args) => api(...args)); Schema hot reloading
  19. How do we add this to the UI layer? System

    Hosting Platform HOSTED_BY
  20. Custom yaml schema name: System description: Any combination of …

    properties: code: type: String required: true useInSummary: true pattern:^(?=.{2,64}$)[a-z0-9]+(?:-[a-z0-9]+)*$ label: Code description: The unique id …
  21. const types = schema.getTypes().map(defineType); const enums = schema.getEnums().map(defineEnum); const queries

    = schema.getTypes().map(defineQueries) return [].concat( types, 'type Query {\n', ...queries, '}', enums, ); Transform to GraphQL SDL
  22. neo4j a poor choice for: - Large documents/blobs - Time

    series - Other things SQL/NoSQL perform well at
  23. import { v1 as neo4j } from 'neo4j-driver'; import {

    makeAugmentedSchema } from 'neo4j-graphql-js'; const driver = neo4j.driver(...); router.use('/graphql', graphqlExpress(() => ({ schema: makeAugmentedSchema({typeDefs}), resolvers: … , context: { driver } }) ) Custom resolvers