Design GraphQL API

Yashod Perera
7 min readMay 21, 2024

--

In this guide I will provide a basic go through on how we can design a proper GraphQL API.

Let’s start with some differences between GraphQL and RestAPI.

Photo by Markus Spiske on Unsplash

What is GraphQL?

GraphQL is a query language which provide more flexibility towards fetching data which will solve over-fetching, under-fetching and utilise the network more efficiently.

As a background GraphQL was developed by Facebook in 2012 and open-sourced since 2015.

For more details please read this.

What we are going to build

Supermarket use case is taken to go through all the following topics and that will help to provide practical examples for each sub section.

Above example is a simple supermarket database sketch where it has multiple stores, items, one store can have many employees etc.

Why we select GraphQL over other protocols?

GraphQL provide flexibility to fetch data in a one go. As an example let’s say we need to fetch all the customers with total number of orders or we need to fetch items for each store and get only the least quantity item on the inventory. How we are going to do it if you are using RestAPI?

Use case 1 — Fetch all the customers with total number of orders

  • In RestAPI we can join two tables and get the total number of orders per each customer.
  • But what if we need the total amount each customer spend or get total discount provided by the store to the customer? Can the RestAPI cater those? That will increase the complexity of the RestAPI and that will not adhere the RestAPI convention at a given point
  • In GraphQL, you have the flexibility to fetch exactly the data you request.

Use case 2 — Fetch information for each store along with the item that has the least quantity

  • In RestAPI first we need to get /stores to get all the stores and then each store items /stores/{store_id}/items?order_by=quantity&sort=des likewise.
  • In GraphQL we can construct the structure in a manner we can get the stores along with the items adding filters.

type Query {
stores: [Store!]!
store(id: ID!): Store
}

type Store {
id: ID!
name: String!
location: String!
items(orderBy: String, limit: Int): [Item!]!
}

type Item {
id: ID!
name: String!
quantity: Int!
}

schema {
query: Query
}

As a summary, GraphQL provides the flexibility to query data and most suitable for data APIs where we expose our data to external consumers.

Quick Overview

This section contains a quick overview of some GraphQL concepts.

  1. Schema — The schema is the core of the GraphQL server where it contains how the data has been exposed and the whole graph can be inspected.
  2. Queries — Data can be fetched using queries and queries can be defined according to the schema.
  3. Mutation — Mutation are used to mutate data in the server. This will use to write, update, delete data to the server which will be similar to POST, PUT, DELETE in restAPI.
  4. Types — GraphQL uses a strong type system since user can only request the defined schema using these types which can be Int , Float , String , Boolean , ID and complex types such as objects, lists etc
  5. Resolvers — Resolvers are the functions which fetch data to each data field in the query.

Following is a sample graphQL query and a response

{
store(id: "1") {
id
name
location
items(orderBy: "quantity") {
id
name
quantity
}
}
}

Response will look likes as follows.

{
"data": {
"store": {
"id": "1",
"name": "Store 1",
"location": "Location 1",
"items": [
{ "id": "101", "name": "Item A", "quantity": 5 },
{ "id": "102", "name": "Item B", "quantity": 10 }
]
}
}
}

Hands on guide to create GraphQL API

Following are the simple steps to design the GraphQL API and create it effectively providing space to future requirements.

  1. Identify the business requirement in more generalize way
  2. Draw the graph hierarchy
  3. Design the graph to full fill business needs by removing implementation details. I repeat do not expose your data structure as it is.
  4. Identify the entry points and design the resolvers properly
  5. Provide some space for pagination, filters

1. Identify the business requirement in more generalize way

First we need to identify what are the data that we are going to expose and what are the business needs that we are going to fullfil. I have listed the business requirement for our system.

  1. List all the items in the website
  2. List items of the store in store system
  3. Customer can check his/her order history
  4. Procurement team can check the lowest inventory for each store
  5. Marketing team analyse customers with total order amounts

It is important to present the data in an organized manner, grouped and relevant to the business domain, rather than exposing the database as it is.

2. Draw the graph hierarchy

This is the next step of identify the data hierarchy according to the business requirement and sketch it as a graph. Please note that you can have multiple graphs depending on the starting point.

Above is having two graphs starting with Store and Customer. This will help to identify the data and how those are distributed.

3. Design the Graph to match the business requirement

Then the most important thing is to design the graph to match the business requirement.

Considering business requirements in the section1 we can clearly identify 3 starting points.

  1. Items — this is for the website to list all the items and this is not having any parent
  2. Stores — There are some requirements to list items in the given store, inventory details for each store
  3. Customers — Customer needs to fetch his/her orders and company need to check each customer stats

Following graphs can be constructed considering the above cases.

Remove implementation details from the graph

As you can see there are some implementation details such as mappings are in the graph which needed to be removed.

After removing database mappings

4. Identify the entry points and design the resolvers properly

Then we have to identify the entry points and design the resolvers properly. Let’s create the Schema for the business requirement. We create the graphQL schema using SDL (schema definition).

Following schema is designed to cater the business requirment. (Please note that this blog only covers the queries. Mutations will be covered in later blog).

type Query {
stores: [Store!]!
store(id: ID!): Store
customers: [Customer!]!
customer(id: ID!): Customer
items: [Item!]!
}

type Store {
id: ID!
name: String!
address: String!
items(orderByQuantity: Boolean): [Item!]!
}

type Item {
id: ID!
name: String!
quantity: Int!
price: Float!
}

type Customer {
id: ID!
name: String!
email: String!
numberOfOrders: Int!
totalExpenses: Float!
orders(limit: Int): [Order!]!
}

type Order {
id: ID!
date: String!
total: Float!
items: [OrderedItem!]!
}

type OrderedItem {
id: ID!
name: String!
quantity: Int!
}

schema {
query: Query
}

let’s evaluate each business requirment and corresponding query.

  • List all the items in the website
{
items {
id
name
quantity
price
}
}
  • List items of the store in store system
{
store(id: "1") {
id
name
address
items {
id
name
quantity
price
}
}
}
  • Customer can check his/her order history
{
customer(id: "1") {
name
email
numberOfOrders
totalExpenses
orders {
date
total
items {
id
name
quantity
}
}
}
}
  • Procurement team can check the lowest inventory for each store
{
stores {
id
name
items(orderByQuantity: true) {
name
quantity
}
}
}
  • Marketing team analyse customers with total order amounts
{
customers {
id
name
email
numberOfOrders
totalExpenses
}
}

5. Provide some space for pagination, filters

Since we are working with data it is important to provide some space to have pagination and also add filters. There are several practices we can follow to make the schema cleaner.

  • keep space for pagination
  • provide filtering capabilities
  • if the input types are complex create record types

Following is the updated SDL adding pagination, filtering.

type Query {
stores(first: Int, skip: Int, filter: StoreFilter): [Store!]!
store(id: ID!): Store
customers(first: Int, skip: Int, filter: CustomerFilter): [Customer!]!
customer(id: ID!): Customer
items(orderByQuantity: Boolean, first: Int, skip: Int, filter: ItemFilter): [Item!]!
item(id: ID!): Item
}

type Mutation {
createStore(input: StoreInput!): Store!
updateStore(id: ID!, input: StoreInput!): Store!
deleteStore(id: ID!): Store!

createItem(input: ItemInput!): Item!
updateItem(id: ID!, input: ItemInput!): Item!
deleteItem(id: ID!): Item!

createCustomer(input: CustomerInput!): Customer!
updateCustomer(id: ID!, input: CustomerInput!): Customer!
deleteCustomer(id: ID!): Customer!

createOrder(customerId: ID!, input: OrderInput!): Order!
updateOrder(id: ID!, input: OrderInput!): Order!
deleteOrder(id: ID!): Order!
}

type Store {
id: ID!
name: String!
address: String!
items(orderByQuantity: Boolean, first: Int, skip: Int, filter: ItemFilter): [Item!]!
}

type Item {
id: ID!
name: String!
quantity: Int!
price: Float!
}

type Customer {
id: ID!
name: String!
email: String!
numberOfOrders: Int!
totalExpenses: Float!
orders(limit: Int, first: Int, skip: Int, filter: OrderFilter): [Order!]!
}

type Order {
id: ID!
date: String!
total: Float!
items: [OrderedItem!]!
}

type OrderedItem {
id: ID!
name: String!
quantity: Int!
}

input StoreInput {
name: String!
address: String!
}

input ItemInput {
name: String!
quantity: Int!
price: Float!
}

input CustomerInput {
name: String!
email: String!
}

input OrderInput {
date: String!
total: Float!
items: [OrderedItemInput!]!
}

input OrderedItemInput {
id: ID!
name: String!
quantity: Int!
}

input StoreFilter {
name: String
address: String
}

input ItemFilter {
name: String
minQuantity: Int
maxQuantity: Int
minPrice: Float
maxPrice: Float
}

input CustomerFilter {
name: String
email: String
minNumberOfOrders: Int
maxNumberOfOrders: Int
minTotalExpenses: Float
maxTotalExpenses: Float
}

input OrderFilter {
date: String
minTotal: Float
maxTotal: Float
}

schema {
query: Query
mutation: Mutation
}

Hope you got the basic understanding how to design a proper GraphQL API and in the next step let’s go through how you can implement this easily. Let’s meet with Implement GraphQL API blog soon.

References

--

--

Yashod Perera

Technical Writer | Tech Enthusiast | Open source contributor