Leveraging Interfaces in GraphQL Schema Design

 

By: Ryan Kanner @CodeProKid

©2020 Copyright - Confidential and Proprietary

About Me

  • Live in Denver, CO

  • Engineer on the Content Platform / WordPress team @ NerdWallet

  • Co-organizer of GraphQL & WordPress meetups in Denver

  • Help maintain WPGraphQL, an open source project bringing GraphQL to WordPress

  • Crazy dog, Blake

  • Baseball Fan

... I also love Pizza

©2020 Copyright - Confidential and Proprietary

In another lifetime

©2020 Copyright - Confidential and Proprietary

Killing the Pizza Game

©2020 Copyright - Confidential and Proprietary

A Perfect Combination

©2020 Copyright - Confidential and Proprietary

+

=

©2020 Copyright - Confidential and Proprietary

Specials Board

query {
  specials: [Pizza]
}

type Pizza {
  id: ID!
  image: String
  name: String!
  sauce: String
  cheese: String
  toppings: [String]
  price: Float!
}

©2020 Copyright - Confidential and Proprietary

Updated Specials Board

type Pizza {
  id: ID!
  image: String
  name: String!
  sauce: String
  cheese: String
  toppings: [String]
  price: Float!
}
type Side {
  id: ID!
  image: String
  name: String!
  sauce: String
  cheese: String
  quantity: Int
  price: Float!
}
interface FoodItem {
  id: ID!
  image: String
  name: String!
  sauce: String
  cheese: String
  price: Float!
}

query {
  specials: [FoodItem!]
}

So, What are Interfaces?

©2020 Copyright - Confidential and Proprietary

An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface

interface FoodItem {
  id: ID!
  name: String!
  price: Float!
  image: String
  sauce: String
  cheese: String
}

©2020 Copyright - Confidential and Proprietary

type Pizza implements FoodItem {
  id: ID!
  name: String!
  price: Float!
  image: String
  sauce: String
  cheese: String
  toppings: [String!]
}

type Side implements FoodItem {
  id: ID!
  name: String!
  price: Float!
  image: String
  sauce: String
  cheese: String
  quantity: Int
}

Querying Interfaces

©2020 Copyright - Confidential and Proprietary

query FEATURED_ITEMS {
  specials {
    __typename
    id
    name
    image
    price
    sauce
    cheese
    ... on Pizza {
      toppings
    }
    ... on Side {
      quantity
    }
  }
}
type Query {
  specials: [FoodItem!]
}

interface FoodItem {
  id: ID!
  name: String!
  price: Float!
  image: String
  sauce: String
  cheese: String
}

type Pizza implements FoodItem {
  ...
  toppings: [String]
}

type Side implements FoodItem {
  ...
  quantity: Int
}

What about unions?

©2020 Copyright - Confidential and Proprietary

type Query {
  specials: [FoodItem!]
}

union FoodItem = Pizza | Side

type Pizza {
  id: ID!
  name: String!
  price: Float!
  image: String
  sauce: String
  cheese: String
  crust: String
}

type Side {
  id: ID!
  name: String!
  price: Float!
  image: String
  sauce: String
  cheese: String
  quantity: Int
}
query FOOD_ITEM_UNION {
  specials {
    __typename
    ... on Pizza {
      id
      name
      price
      image
      sauce
      cheese
      crust
    }
    ... on Side {
      id
      name
      price
      image
      sauce
      cheese
      quantity
    }
  }
}

The difference visualized

©2020 Copyright - Confidential and Proprietary

FoodItem

  • id
  • name
  • price
  • image
  • sauce
  • cheese

Pizza

- toppings

Side

- quantity

FoodItem

Pizza

  • id
  • name
  • price
  • image
  • sauce
  • cheese
  • toppings

Side

  • id
  • name
  • price
  • image
  • sauce
  • cheese
  • quantity

Interfaces

Unions

What about something a little more complex?

©2020 Copyright - Confidential and Proprietary

©2020 Copyright - Confidential and Proprietary

Another change to the specials

type Pizza {
  id: ID!
  image: String
  name: String!
  price: Float!
  sauce: String
  cheese: String
  toppings: [String]
}
type Side {
  id: ID!
  image: String
  name: String!
  price: Float!
  sauce: String
  cheese: String
  quantity: Int
}
type Apparel {
  id: ID!
  image: String
  name: String!
  price: Float!
  inStock: Boolean!
  description: String
}

Multiple Interfaces

©2020 Copyright - Confidential and Proprietary

interface FoodItem {
  sauce: String
  cheese: String
}
type Apparel implements 
  Product {
    id: ID!
    name: String!
    price: Float!
    image: String
    inStock: Boolean!
    description: String
}
interface Product {
  id: ID!
  name: String!
  price: Float!
  image: String
}
type Pizza implements 
  FoodItem & Product {
    id: ID!
    name: String!
    price: Float!
    image: String
    sauce: String
    cheese: String
    toppings: [String]
}
type Side implements 
  FoodItem & Product {
    id: ID!
    name: String!
    price: Float!
    image: String
    sauce: String
    cheese: String
    quantity: Int
}

Multiple Interfaces Query

©2020 Copyright - Confidential and Proprietary

type Query {
  specials: [Product!]
}
query FEATURED_ITEMS {
  specials {
    __typename
    id
    name
    image
    price
    ... on FoodItem {
      sauce
      cheese
    }
    ... on Pizza {
      toppings
    }
    ... on Side {
      quantity
    }
    ... on Apparel {
      inStock
      description
    }
  }
}

Pizza

Apparel

Side

Component Mappings

©2020 Copyright - Confidential and Proprietary

<ProductCard>

fragment pf on Product {
  __typename
  id
  name
  price
  image
  ... foodFields
  ... apparelFields
}

<FoodItemDetails>

fragment foodFields 
  on FoodItem {
    __typename
      sauce
      cheese
    ... on Pizza {
      toppings
    }
    ... on Side {
      quantity
      dippingSauce: sauce
    }
}

<ApparelDetails>

fragment apparelFields 
  on Apparel {
    inStock
    description
}

©2020 Copyright - Confidential and Proprietary

Multiple DataType Handling Without GraphQL

Pizza

GET /api/v99/pizza/1

Apparel

GET /api/v99/apparel/2

Side

GET /api/v99/side/3

Takeaways

©2020 Copyright - Confidential and Proprietary

  • Interfaces help bring your Type's together, and make your Graph more flexible.
     
  • Make your components more re-usable.
     
  • Using multiple interfaces with Types can unlock powerful Query combinations.
     
  • A tradeoff of using interfaces is that you have to code defensively on the client.
     
  • PizzaQL 4 Life.

Thanks for coming! Questions?

©2020 Copyright - Confidential and Proprietary

Leveraging Interfaces in GraphQL Schema Design

By Ryan Kanner

Leveraging Interfaces in GraphQL Schema Design

  • 1,168