/
GraphQL development best practices

GraphQL development best practices

The following are recommendations for how to design new APIs with the intention of promoting consistent usage.

Use of input types

We strongly recommend using input types to define the expected input, rather than a large number of individual arguments. This makes the schema more readable and maintainable. For example, instead of this:

""" bad example do not do this """
extend type Mutation {
  core_widget_create_widget(
    name: String!
    description: String!
    type: param_alphaext!
    """ Potentially many more arguments here """ 
  ): core_widget_widget_result!
}

Create a dedicated input type like this:

input core_widget_widget_input {
  name: String!
  description: String!
  type: param_alphaext! 
  """ Potentially many more arguments here """ 
}

extend type Mutation {
  core_widget_create_widget(
    widget: core_widget_widget_input
  ): core_widget_widget_result!
}

When authoring the schema, you should strive to define input types that could potentially be reused in multiple queries or mutations (even if they are not at the time of creation). For example, if later you decided to add another mutation which allowed you to create a widget and assign it to an (optional) category at the same time, don't create a new input type like this:

" bad example do not do this "
input core_widget_widget_with_category_input {
  name: String!
  description: String!
  type: param_alphaext!
  category_id: core_id!
}

extend type Mutation {
  core_widget_create_widget_in_category(
    widget: core_widget_widget_with_category_input
  ): core_widget_widget_result!
}

This leads to unnecessary duplication. Instead, using a new argument and reusing the existing input type would make more sense:

input core_widget_widget_input {
  name: String!
  description: String!
  type: param_alphaext!
}

extend type Mutation {
  core_widget_create_widget_in_category(
    widget: core_widget_widget_input
    category_id: core_id!
  ): core_widget_widget_result!
}

Note: this is just a "toy" example. Another valid approach would be to add category_id as an optional property on the input type and reuse the existing create mutation, but if you were going to have a separate mutation you should follow the second approach in terms of input types.

In other situations, separate input types are unavoidable. For example, perhaps some fields are required during creation, but are optional when updating, and you want to enforce that via the types.

Use of result types

Similar to input types above, our strong preference is to return results via a dedicated result type, rather than using the type itself as the return value. For example, when requesting a set of widgets, instead of returning an array of widget types:

type core_widget_widget {
  id: core_id!
  name: String!
  description: String!
  type: param_alphaext!
}


extend type Query {
  core_widget_my_widgets(): [core_widget_w