GraphQL Design Pattern (1): Pagination & Summary

So I designed a schema for a common feature,
basically frontend needs to display two things:
  • Summary of items
  • The items
  • Suppose the page is about showing a list of
    animals, and we want to show them the summary of
    each type of animals.
    FILTERS
    [ ] Mammal (200)
    [x] Birds (110)
    [ ] Fish (200)
    
    ITEMS
    - Eagle
    - Pigeon
    - Stork
    - ...
    Initial Design
    Initially, the schema I designed is as follows:
    query {
      displayAnimals(animalTypes: [AnimalType!]!): DisplayAnimalsResult!
    }
    
    enum AnimalType {
      MAMMAL
      BIRD
      FISH
    }
    
    type DisplayAnimalsResult {
      summary: DisplayAnimalsResultSummary!
      animals: FilteredAnimals!
    }
    
    type DisplayAnimalsResultSummary {
      mammal: Int!
      bird: Int!
      fish: Int!
    }
    
    type FilteredAnimals {
      totalCount: Int!  
      paginate(pagingOptions: PagingOptions!): [Animal!]!
    }
    
    type PagingOptions {
      currentPage: Int!
      itemsPerPage: Int!
    }
    
    type Animal {
      name: String!
    }
    And the frontend query:
    query DisplayAnimal(
      $animalTypes: [AnimalType!]!
      $pagingOptions: PagingOptions!
    ) {
      displayAnimals(animalTypes: $animalTypes) {
        summary {
          mammal
          bird
          fish
        }
        animals {
          totalCount
          paginate(pagingOptions: $pagingOptions) {
            name
          }
        } 
      }
    }
    The Problem
  • The summary is hardcorded. If frontend needed a new kind of summary, the schema has to change.
  • The summary property feels like a boilerplate to implement.
  • A Better Design
    After seeing this pattern over and over and getting
    frustrated, I suddenly realised that the summary
    property is actually not needed!
    Let's see the new schema (note that DisplayAnimalsResult and DisplayAnimalsResultSummary are gone):
    query {
      displayAnimals(animalTypes: [AnimalType!]!): FilteredAnimals!
    }
    
    enum AnimalType {
      MAMMAL
      BIRD
      FISH
    }
    
    type FilteredAnimals {
      totalCount: Int!  
      paginate(pagingOptions: PagingOptions!): [Animal!]!
    }
    
    type PagingOptions {
      currentPage: Int!
      itemsPerPage: Int!
    }
    
    type Animal {
      name: String!
    }
    And the frontend query (here's the magic, using
    GraphQL Property Alias):
    query DisplayAnimal(
      $animalTypes: [AnimalType!]!
      $pagingOptions: PagingOptions!
    ) {
      mammal: displayAnimals(animalTypes: [MAMMAL]) {
        totalCount
      }
      bird: displayAnimals(animalTypes: [BIRD]) {
        totalCount
      }
      fish: displayAnimals(animalTypes: [FISH]) {
        totalCount
      }
      displayAnimals(animalTypes: $animalTypes) {
        animals {
          totalCount
          paginate(pagingOptions: $pagingOptions) {
            name
          }
        } 
      }
    }
    How is this better?
  • No need to implement summary resolver on backend, leading to lesser code.
  • The summary is no longer hardcoded, e.g. you can query total count of birds and fishes.
  • Smaller schema, less noise.
  • Conclusion
    GraphQL Alias is a powerful feature, thus remember to incorporate it when designing the schema.
    P/S
    Actually I wrote this for my workplace team. I decided to post it here because maybe others might find it helpful.

    23

    This website collects cookies to deliver better user experience

    GraphQL Design Pattern (1): Pagination & Summary