Agenda

  • What is GraphQL? Brief history
  • Comparison with REST
  • Sample use cases:
    • Cost import
    • DecisionsExpanded with ProjectFilter
    • Frontend
  • Comparison with Redux
  • Potential benefits for Antura Projects

GraphQL

Developed at Facebook 2012-2015 as a way to facilitate the company-wide strategy shift to mobile

Extremely fast adoption across the industry after the public release

Solves concrete practical problems, which might explain the growing popularity and fast adoption rates

 

GraphQL vs REST

GET /api/v1/users

Request

POST /graphql/v1

query {
  users {
    id
    fullName
  }
}

GraphQL vs REST

{
  users: [
    {
      id: 1234
      fullName: "Glenn Nilsson"
    }
  ]
}

Response

So what? 🤨

REST

Endpoint + Type (Shape)

The shape of data is defined by the endpoint and is static

GET /users/1 
{ id, firstName, lastName, login, email, address, ... }

E.g. WBS-Rapport BasicUserInfo

REST

  • Many endpoints for different use-cases
  • Fetching related data with multiple calls

More code

More traffic

Higher complexity

Problems

GraphQL

Query doesn't enforce response shape

query {
  users {
    id
    firstName
    lastName
    email
  }
}
query {
  users {
    fullName
    login
  }
}

GraphQL

Related data in one request

query {
  projects {
    id
    name
    sponsor {
      fullName
      email
    }
    projectManager {
      name
    }
  }
}
1. api/v1/projects
   -> where ProjectNumber == {ProjectNumber}

2. projectId, taskSetId
   -> api/v1/projects/{projectId}/tasksets/{taskSetId}/tasks
   
3. api/v1/propertydefinitions
   -> where Name == {TaskReferenceKey}

4. For each taskId:
   /api/v1/tasks/{taksId}/propertyvalues/{propertyDefinitionId}
   -> where task.StringValue == {TaskReferenceValue}
	

Cost import via our REST API

Task matching

Task matching with GraphQL

{
  tasks(where: {
        task_property_values: {
          _and: [
            {task_property_definition:
              {name: {_eq: "IntegrationStandard_TASKREF"}}},
            {string_value: {_eq: "01"}}
          ]}})
  {
    id
    name
    task_set {
      project {
        project_number
        name
      }
    }
    task_property_values {
      string_value
      task_property_definition {
        name
      }
    }
  }
}

GraphQL Schema

  • Server-side
  • Own language - GraphQL SDL
  • Describes what data is available
  • Describes how to fetch/update data

Schema First

Type system for the API

Types (server-side)

  • API return data​​
  • Fields & Relationships
type User {
  id: Int!
  firstName: String!
}

type Project {
  id: Int!
  name: String!
  projectManager: User!
  sponsor: User
}

Queries & Mutations

type Query {
  getUsers: [User]
  getProjects: [Project]
  getUserById(id: Int!): User
}


type Mutation {
  deleteUser(id: Int!): User
}

All queries under type Query

All mutations under type Mutation

Mutations

Update project currencyId by projectID

  • REST: PATCH

  • RPC

  • GET + PUT

Mutations

GET /api/v1/projects/{id}
{
  "name": "ProjectName",
  "projectNumber": "12345",
  "deleted": false,
  "useResourceRequest": true,
  "currencyID": 1,
  "lastPublishedTaskSetID": null,
  "editTaskSetID": 2,
  "latestOfficialTaskSetID": 2,
  "projectTypeID": 1,
  "phaseIndex": 0,
  "sponsorId": 55,
  "managerId": 66,
  "usePlanning": true,
  "canEditAfterFinish": true,
  "isTemplateProject": true,
  "id": 123
}

REST: Update project currencyId by projectID

PUT /api/v1/projects/{id}
{
  "name": "ProjectName",
  "projectNumber": "12345",
  "deleted": false,
  "useResourceRequest": true,
  "currencyID": 2,
  "lastPublishedTaskSetID": null,
  "editTaskSetID": 2,
  "latestOfficialTaskSetID": 2,
  "projectTypeID": 1,
  "phaseIndex": 0,
  "sponsorId": 55,
  "managerId": 66,
  "usePlanning": true,
  "canEditAfterFinish": true,
  "isTemplateProject": true,
  "id": 123
}

Mutations

POST /v1/graphql

mutation {
  update_projects(
    where: {id: {_eq: 123}},
    _set: {currency_id: 2}
  )
  {
    affected_rows
  }
}

GraphQL: Update project currencyId by projectID

GraphQL Schema

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

So what?

GraphQL Clients 

  • Validation at build time

  • Automatic type generations (e.g. TS)

  • Live documentation (like Swagger)

  • Tooling, intellisense

Download schema definition

GraphiQL 

Demo: Hasura, GraphiQL, Insomnia

Introspection query

Client

Introspection query
Schema in json

Query to fetch all schema

Typescript definitions

Demo Frontend

GraphQL Client for React

+ Cache

GraphQL - Frontend 

Before

  • Types
  • Decoder
  • ApiCaller
  • DataProvider

After

  • Types from schema
  • Automatic validation
  • Call GraphQL API

Manually

GraphQL - Frontend 

Apollo cache

  • Components that fetch own data

  • Avoid unnecessary API calls

  • Like redux, but much less code

GraphQL

  • One endpoint

  • Response shape is flexible and defined by the request

  • Smaller payload

  • Strongly typed vs JSON in REST

  • Tooling/discoverability

  • Developer experience

React Views

Redux Store

Single Source of Truth

getState()

dispatch()

subscribe()

Thunks

API

Redux

React Views

Redux Store

Single Source of Truth

getState()

dispatch()

subscribe()

Thunks

API

Connect HoC

Redux

React Views

Server

 

 

queries()

mutations()

subscriptions()

Render Props

GraphQL

React Views

Server/DB

Single Source of Truth

queries()

mutations()

subscriptions()

Render Props

GraphQL API

Similar Application Structure to Redux

Much simpler in practice

Potential benefits for AP

  • Simpler frontend architecture
  • Less traffic between frontend and backend
  • Fewer API calls (recent support ticket)
  • Integrations: mutations as one operation can potentially allow transactions

Adoption strategies

  • (Backend) GraphQL endpoints coexisting with REST
  • (Backend) GraphQL layer on top of existing REST endpoints
  • (Frontend) GraphQL layer on top of existing REST endpoints 

Questions?

G

By margaretkru

G

  • 147