Reduce GraphQL Ruby mutation arguments in resolve method

Published: by Creative Commons Licence

GraphQL Ruby gem helps to add GraphQL support to Ruby or Ruby on Rails applications.

The official guides or How to GraphQL website would be a good starting point.

The Basics

Here is a basic sign up mutation that handles the sign up process:

class Mutations::SignUp < Mutations::BaseMutation
  null true

  argument :email, String, required: true
  argument :password, String, required: true

  field :token, String, null: true
  field :user, Types::UserType, null: true

  def resolve(email:, password:)
    # create your user with passed in email and password.
  end
end

The Problem

But what if it's a third party client calling this mutation endpoint with a lot of parameters?

Here is an example (the parameters are illustrative only, which may not make total sense).

Mutation's resolve function would now have 10+ parameters.

class Mutations::SignUp < Mutations::BaseMutation
  null true

  argument :email, String, required: true
  argument :password, String, required: true
  argument :expires_at, GraphQL::Types::BigInt, required: true
  argument :expires_in, GraphQL::Types::BigInt, required: true
  argument :resource_state, Int, required: true
  argument :username, String, required: false
  argument :firstname, String, required: true
  argument :lastname, String, required: true
  argument :sex, String, required: false
  argument :active_till, GraphQL::Types::ISO8601DateTime, required: true

  field :token, String, null: true
  field :user, Types::UserType, null: true

  def resolve(email:, password:, expires_at:, expires_in:, resource_state:,
              username:, firstname:, lastname:, lastname:, sex:, active_till:)
    # create your user with data passed in.
  end
end

The Solutions

Use double splat operator (**)

Ruby 2.0 introduced double splat operator captures all keyword arguments.

class Mutations::SignUp < Mutations::BaseMutation
  null true

  argument :email, String, required: true
  argument :password, String, required: true
  argument :expires_at, GraphQL::Types::BigInt, required: true
  argument :expires_in, GraphQL::Types::BigInt, required: true
  argument :resource_state, Int, required: true
  argument :username, String, required: false
  argument :firstname, String, required: true
  argument :lastname, String, required: true
  argument :sex, String, required: false
  argument :active_till, GraphQL::Types::ISO8601DateTime, required: true

  field :token, String, null: true
  field :user, Types::UserType, null: true

  def resolve(**arguments)
    # create your user with data passed in.
    # arguments is a hash like this: { email: '', password: '', expires_at: 1234556, ... }
    # use arguments[:email] to access them.
  end
end

Create a new Type

https://graphql-ruby.org/guides#type-definitions-guides

module Types
  class SignUpArguments < Types::BaseInputObject
    argument :expires_at, GraphQL::Types::BigInt, required: true
    argument :expires_in, GraphQL::Types::BigInt, required: true
    argument :resource_state, Int, required: true
    argument :username, String, required: false
    argument :firstname, String, required: true
    argument :lastname, String, required: true
    argument :sex, String, required: false
    argument :active_till, GraphQL::Types::ISO8601DateTime, required: true
  end
end

class Mutations::SignUp < Mutations::BaseMutation
  null true

  argument :email, String, required: true
  argument :password, String, required: true
  argument :sign_up_details, Types::SignUpArguments, required: true

  field :token, String, null: true
  field :user, Types::UserType, null: true

  def resolve(email:, password:, sign_up_details:)
    # create your user with data passed in.
    # sign_up_details.to_h => { expires_at: 1234556, ... }
  end
end