Rethinking rest practices with graphql

@mwarger

Hi, I'm mat warger

professional coder

our job is creating value for clients and customers

our job is lists

What's the plan?

  • Why would we use GraphQL in contrast to REST APIs?
  • What is GraphQL and how does it work?
  • How can you get started with GraphQL in your apps?

WHY GRAPHQL?

WHY not rest?

what is a rest api?

A web api communicating via http that attempts to conform to an architecture pattern -
representational state transfer

Questions

  • How strictly should you adhere to the principles of REST? How do you decide which ones count?

  • How should you handle versioning? Should you bother?

  • How do you want to structure your objects? What is the shape of the data that works best for the clients of your API?

Questions

  • Are you sending the appropriate data to your users? Are you sending them information they don’t need?

  • Concerning relational or hierarchical data, are they able to efficiently query for what they need from nested structures?

  • Are users able to easily figure out what API endpoints are available and how they should be used?

what is GRAPHQL?

get a list of movies

GET: /movies
[{
  id: "123",
  name: "The Matrix"
}, 
{
  id: "456",
  name: "The Avengers"
}]      

Rest

{
  movies: [{
    id: "123",
    name: "The Matrix"
  }, 
  {
    id: "456",
    name: "The Avengers"
  }]      
}
POST: /graphql
{
  movies {
    id
    name
  }
}

Graphql

get a movie

{
  id: "123",
  name: "The Matrix"
}
GET: /movies/123

Rest

{
  id: "123",
  name: "The Matrix"
}     
POST: /graphql
{
  movies(id: "123") {
    id
    name
  }
}

graphql

CREATE a movie

Rest

POST: /movies/123
{
  name: "The Matrix"
}
{
  id: "123",
  name: "The Matrix"
}      

graphql

POST: /graphql
mutation {
  createMovie(title: "Star Wars") {
    id
    title
  }
}
{
  id: "789",
  name: "Star Wars"
}      

Common myth

graphql requires replacement of your existing api

graphql can be used with

  • Your existing services
  • Your existing database
  • No database at all
  • Your existing REST APIs
  • Your existing GraphQL APIs
  • External REST APIs
  • External GraphQL APIs
  • Anything else...

Resolvers

REST ENDPOINT

  • A specific endpoint serves a specific response
  • The response is determined by the server
  • Any change needed in the response requires a change on the server
  • Typically handled using a controller method in a web API framework (e.g. Express, .NET, Spring)

graphql resolver

  • A single endpoint resolves multiple queries and mutations
  • The response is determined by the query
  • Any change needed in the response requires a change in the query
  • Typically handled using a resolver function in a GraphQL API framework (e.g. Express/Apollo, .NET, Spring)

API Endpoints

GraphQL Types

Controllers

Data

Resolvers

Data

Graphql SCHEMA

queries, mutations, and types

Schema

  • Types are defined for the entities you want to represent and expose in your API
  • Queries and mutations are defined for the operations you want to expose
    • They can return single items or arrays
    • They can accept arguments
    • Contraints can be put in place for nullability
    • Described with plain language 
  type Movie {
    id: ID!
    title: String!
    favorite: Boolean
  }

  type Query {
    nowPlaying: [Movie!]
    movieById(id: ID!): Movie
  }

  type Mutation {
    toggleFavoriteMovie(movieId: ID!): Movie
  }
GET: /movies
[{
  id: "123",
  name: "The Matrix"
}, 
{
  id: "456",
  name: "The Avengers"
}]      
{
  movies: [
  {
    id: "123",
    name: "The Matrix",
    favorite: true,
    cast: [111, 222, 333]
  }, 
  {
    id: "456",
    name: "The Avengers",
    favorite: true,
    cast: [345, 456, 567]
  }
 ]      
GET: /movies

GET: /movies/123/cast
GET: /movies/456/cast

GET: /movies?include=cast

POST: /movies/123
POST: /moviesAndCast
POST: /moviesAndCastAndStuff
[{
  id: "111",
  name: "Keanu Reeves",
  characterName: "Neo"
}, ...]
[{
  id: "345",
  name: "Scarlett Johansson",
  characterName: "Natasha Romanoff"
}, ...]
{
  movies: [
  {
    id: "123",
    name: "The Matrix",
    favorite: true,
    cast: [
      {
        id: 111,
        name: "Keanu Reeves",
        characterName: "Neo"
      }
    ]
  }, 
  {
    id: "456",
    name: "The Avengers",
    favorite: true,
    cast: [
    {
      id: "1",
      name: "Robert Downey Jr",
      characterName: "Tony Stark"
    },
    {
      id: "2",
      name: "Scarlett Johansson",
      characterName: "Natasha Romanoff"
    },
    ]
  },
  {...}
 ]      
GET: /movies

GET: /movies/123/cast
GET: /movies/456/cast

GET: /movies?include=cast

POST: /movies/123
POST: /moviesAndCast
POST: /moviesAndCastAndStuff
{
  movies: [
  {
    id: "123",
    name: "The Matrix",
    favorite: true,
    cast: [
      {
        id: 111,
        name: "Keanu Reeves",
        characterName: "Neo"
      }
    ]
  }, 
  {
    id: "456",
    name: "The Avengers",
    favorite: true,
    cast: [
    {
      id: "1",
      name: "Robert Downey Jr",
      characterName: "Tony Stark"
    },
    {
      id: "2",
      name: "Scarlett Johansson",
      characterName: "Natasha Romanoff"
    },
    ]
  },
  {...}
 ]      
GET: /movies

GET: /movies/123/cast
GET: /movies/456/cast

GET: /movies?include=cast

POST: /movies/123
POST: /moviesAndCast
POST: /moviesAndCastAndStuff
{ 
  movies: [{
    id: "123",
    name: "The Matrix"
  },  
  {
    id: "456",
    name: "The Avengers"
  }]      
}
POST: /graphql
{
  movies {
    id
    name
  }
}
{
  movies: [
  {
    id: "123",
  }, 
  {
    id: "456",
  }
 ]      
POST: /graphql
{
  movies {
    id
  }
}
POST: /graphql
{
  movies {
    id
    name
    favorite
    cast {
      id
      name
      characterName
    }
  }
}
{
  movies: [
  {
    id: "123",
    name: "The Matrix",
    favorite: true,
    cast: [
      {
        id: 111,
        name: "Keanu Reeves",
        characterName: "Neo"
      }
    ]
  }, 
  {
    id: "456",
    name: "The Avengers",
    favorite: true,
    cast: [
    {
      id: "1",
      name: "Robert Downey Jr",
      characterName: "Tony Stark"
    },
    {
      id: "2",
      name: "Scarlett Johansson",
      characterName: "Natasha Romanoff"
    },
    ]
  },
  {...}
 ]      
  type Movie {
    id: ID!
    title: String!
    favorite: Boolean @deprecated(reason: "No longer needed.")
  }

  type Query {
    nowPlaying: [Movie!]
    movieById(id: ID!): Movie
  }

  type Mutation {
    toggleFavoriteMovie(movieId: ID!): Movie
  }

What does it look like?

demo

the bigger picture

types === tools

Platforms

  • AppSync - https://aws.amazon.com/appsync/
  • Hasura - https://hasura.io/
  • Nhost - https://nhost.io/

Server

  • Apollo - https://www.apollographql.com/docs/apollo-server/
  • NestJS - https://docs.nestjs.com/graphql/quick-start
  • Prisma + Nexus - https://github.com/graphql-nexus/nexus-plugin-prisma
  • .NET - https://graphql-dotnet.github.io/
  • Java/Spring - https://netflix.github.io/dgs/

Client

  • Apollo Client - https://www.apollographql.com/docs/react/
  • urql - https://formidable.com/open-source/urql/
  • GQty - https://gqty.dev/
  • Amplify Datastore - https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js/

Resources

Thank you!

@mwarger

slides + code

Rethinking Rest Practices with GraphQL

By Mat Warger

Rethinking Rest Practices with GraphQL

Code demos discussed are here..https://github.com/mwarger/graphql-typescript-react-demo

  • 1,269