GraphQL & UI5

Shape your requests

Efim Parshin, BDO Unicon Business solutions

June 28, 2019

About me

Typical REST Approach

GET /cart/1

GET /goods/1

GET /goods/2

GET /goods/3

GET /image/1

GET /image/2

GET /image/3

[{amount: 2, price: 549, goods_id: 1}]
{name: 2, color: White}

7

roundtrips

Complex endpoints

GET /cart/1?expand=items

Over-fetching

GET /cart/1?fields=count,price,liked

GET /cart/1?fields=count,price,liked&expand=items(name,color)

Custom endpoints

 /very_special_endpoint_with_all_required_data

/very_special_endpoint_with_all_required_data_but_for_mobile

 /same_as_mobile_but_with_desciptions_and_discounts

/v2

GraphQL

It isn't!

  • Graph database
  • It's not database at all
  • Library

It's

  • A data query language
  • Just spec
  • Works as additional layer between client and server

Implementations

Demo time!

GraphQL & UI5

Simple ajax query

jQuery.ajax('https://swapi.graph.cool/', {
          method: 'POST',
          data: JSON.stringify({
            query: `{
              allFilms {
                title
                planets {
                  name
                }
              }
            }`
          }),
          contentType: 'application/json; charset=utf-8',
          dataType: 'json',
          success: function(resp) {
            if(resp.errors) {
              //handle errors
            } else {
              //handle success
            }
          }
        });

GraphQL model?

Mutations

{
    query {...}
    mutation {
        createPlanet(name: "UI5Planet") {
            id
            name
        }
    }
}

Main features

  • Single endpoint

  • Schema based

  • Database agnostic

  • Introscpection

  • Awesome tooling

Addtional features

  • Fragments

  • Interfaces

  • Subscriptions

  • @Defer

Conclusion

Query language

{
  query {
    allFilms {
      title
      director
      planets {
        name
      }
    }
  }
}
{
    "data": {
        "allFilms": [
          {
            "title": "A New Hope",
            "director": "George Lucas",
            "planets": [
              {
                "name": "Alderaan"
              },
               ...
            ]
          },
          {
            "title": "Attack of the Clones",
            "director": "George Lucas",
            "planets": [
              {
                "name": "Naboo"
              },
                ...
            ]
          }
            ...
        ]
    }
}

Query arguments & aliases

{
  "data": {
    "Film": {
      "name": "The Empire Strikes Back",
      "director_name": "Irvin Kershner"
    },
    "Most_loved_film": {
      "title": "A New Hope",
      "director": "George Lucas",
      "planets": [
        {
          "name": "Alderaan"
        },
        {
          "name": "Hoth"
        }
      ]
    }
  }
}
{
  Film(title: "The Empire Strikes Back") {
    name: title
    director_name: director
  }
  
  Most_loved_film: Film(id: "cj0nxmy3fga5s01148gf8iy3c") {
    title
    director
    planets(first: 2 orderBy: name_ASC) {
      name
    }
  }
}

Type system

type Film {
  createdAt: DateTime!
  director: String
  episodeId: Int!
  id: ID!
  isPublished: Boolean!
  openingCrawl: String
  planets(
    filter: PlanetFilter
    orderBy: PlanetOrderBy
    skip: Int
    after: String
    before: String
    first: Int
    last: Int
  ): [Planet!]
  producers: [String!]
  releaseDate: DateTime
  title: String!
  updatedAt: DateTime!
}
{
  allFilms {
    title
    director
    planets {
      name
    }
  }
}
type Planet {
  climate: [String!]
  createdAt: DateTime!
  diameter: Int
  films(
    filter: FilmFilter
    orderBy: FilmOrderBy
    skip: Int
    after: String
    before: String
    first: Int
    last: Int
  ): [Film!]
  gravity: String
  id: ID!
  isPublished: Boolean!
  name: String!
  orbitalPeriod: Int
  population: Float
  rotationPeriod: Int
  surfaceWater: Float
  terrain: [String!]
  updatedAt: DateTime!

Query

Film type

Planet type

Resolving fields

{
    allFilms: (obj, args, ctx) => {
        const query_args = resolveArgs(args);
        return db.films.findAll(query_args);
    }

    Film: {
        title: (obj) => { return obj.title },
        planets: (obj, args, ctx) => { 
           return db.planets.findAll({
                where: {
                    film_id: obj.id
                }
           });
        }
    } 
}

Introspection

{
    __schema {
        
    }
}

Drawbacks

  • N+1 Query

  • Caching

  • Security

  • File upload

N+1 Query

{
    allFilms(first: 3) {
        title
        planets {
            name
        }
    }
}

SELECT * FROM films LIMIT 3

SELECT * FROM planets where planet_ids <@ 1

SELECT * FROM planets where planet_ids <@ 2

SELECT * FROM planets where planet_ids <@ 3

Made with Slides.com