Design decisions in GraphQL

Things to consider when creating/changing the schema

All of the following schema changes are potentially breaking changes:

  • Removing a type or field
  • Renaming a type or field
  • Adding nullability to a field
  • Removing a field’s arguments
  • It’s easier and safer to add a new field to a schema than it is to remove an existing field that some of your clients are using.

Query driven design

Design your schema based on how data is used (aka Query-driven schema design), not based on how it’s stored.

If your client wants to query that:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
query EventList {
upcomingEvents {
name
date
location {
name
weather {
temperature
description
}
}
}
}
query EventList { upcomingEvents { name date location { name weather { temperature description } } } }
query EventList {
  upcomingEvents {
    name
    date
    location {
      name
      weather {
        temperature
        description
      }
    }
  }
}

Then your schema could look like that:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type Query {
upcomingEvents: [Event!]!
}
type Event {
name: String!
date: String!
location: Location
}
type Location {
name: String!
weather: WeatherInfo
}
type WeatherInfo {
temperature: Float
description: String
}
type Query { upcomingEvents: [Event!]! } type Event { name: String! date: String! location: Location } type Location { name: String! weather: WeatherInfo } type WeatherInfo { temperature: Float description: String }
type Query {
  upcomingEvents: [Event!]!
}

type Event {
  name: String!
  date: String!
  location: Location
}

type Location {
  name: String!
  weather: WeatherInfo
}

type WeatherInfo {
  temperature: Float
  description: String
}

Naming conventions

  • Field names should use camelCase
  • Type names should use PascalCase
  • Enum names should use PascalCase
  • Enum values should use ALL_CAPS

In GraphQL, it’s recommended for every mutation’s response to include the data that the mutation modified. This enables clients to obtain the latest persisted data without needing to send a followup query.

Error handling

Mutations are much more likely than queries to cause errors, because they modify data. A mutation might even result in a partial error, in which it successfully modifies one piece of data and fails to modify another. It is recommended to define a MutationResponse interface in your schema.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
interface MutationResponse {
code: String!
success: Boolean!
message: String!
}
interface MutationResponse { code: String! success: Boolean! message: String! }
interface MutationResponse {
  code: String!
  success: Boolean!
  message: String!
}

and implemented:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type UpdateUserEmailMutationResponse implements MutationResponse {
code: String!
success: Boolean!
message: String!
user: User
}
type UpdateUserEmailMutationResponse implements MutationResponse { code: String! success: Boolean! message: String! user: User }
type UpdateUserEmailMutationResponse implements MutationResponse {
  code: String!
  success: Boolean!
  message: String!
  user: User
}

Descriptions (docstrings)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
"Description for the type"
type MyObjectType {
"""
Description for field
Supports **multi-line** description for your [API](http://example.com)!
"""
myField: String!
otherField(
"Description for argument"
arg: Int
)
}
"Description for the type" type MyObjectType { """ Description for field Supports **multi-line** description for your [API](http://example.com)! """ myField: String! otherField( "Description for argument" arg: Int ) }
"Description for the type"
type MyObjectType {
  """
  Description for field
  Supports **multi-line** description for your [API](http://example.com)!
  """
  myField: String!

  otherField(
    "Description for argument"
    arg: Int
  )
}

About Author

Mathias Bothe To my job profile

I am Mathias from Heidelberg, Germany. I am a passionate IT freelancer with 15+ years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I create Bosycom and initiated several software projects.