Rails Kitchen

It's a place to write on stuff I learned recently.

GraphQL Ruby Mutation With Input Object Type

| Comments

In the previous blog post about GraphQl mutation, we specified each field in input as input_field. This method will do the job but it has some issues.

Consider an example mutation for saving an article. Here we used the input_field method without InputObjectType.
app/graphql/mutations/article_mutations.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# encoding: utf-8
module ArticleMutations
  Create = GraphQL::Relay::Mutation.define do
    name 'AddArticle'

    input_field :title, !types.String
    input_field :body, !types.String

    # Define return parameters
    return_field :article, ArticleType
    return_field :errors, types.String

    resolve lambda { |object, inputs, ctx|
      article = Article.new(title: inputs[:title], body: inputs[:body])

      if article.save
        { article: article }
      else
        { errors: article.errors.to_a }
      end
    }
  end
end
1
2
3
4
5
6
7
8
9
10
mutation addArticle{
  addArticle(input: { title: "GraphQL mutation", body: " This article is about graphql mutation using  InputObjectType" })
  {
    article{
      id
      title
      body
    }
  }
}
For saving this we need to instantiate article object by assigning each and every input params like this.
app/graphql/mutations/article_mutations.rb
1
article = Article.new(title: inputs[:title], body: inputs[:body])
This method become ugly if our object contain lots fields. We can define and utilize mutation input params in the better way using InputObjectType. If we can specify article object as input_field instead of each field we can save object like below.
app/graphql/mutations/article_mutations.rb
1
article = Article.new(inputs[:article].to_h)
which is maintanable and easy to read. Lets see how we can achive this.
The first step is to define InputObjectType and declare that input object type as input_field. Here we are going to create InputObjectType in another folder inside our graphql folder for maintainability.
app/graphql/input_objects/article_input_object_type.rb
1
2
3
4
5
ArticleInputObjectType = GraphQL::InputObjectType.define do
  name 'ArticleInput'
  input_field :title, !types.String
  input_field :body, !types.String
end
Since we created new folder for input_objects, we have to tell Rails to autoload paths. place below code in application.rb to autoload it.
config/application.rb
1
config.autoload_paths << Rails.root.join('app/graphql/input_objects')
Now we can declare ArticleInputObjectType as input_field inside our mutation and use declared input_field inside our mutation resolver to save the article. So final mutation definition will look like this.
app/graphql/mutations/article_mutations.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# encoding: utf-8
module ArticleMutations
  Create = GraphQL::Relay::Mutation.define do
    name 'AddArticle'

    # Define input parameters
    input_field :article, !ArticleInputObjectType

    # Define return parameters
    return_field :article, ArticleType
    return_field :errors, types.String

    resolve lambda { |object, inputs, ctx|
      article = Article.new(inputs[:article].to_h)

      if article.save
        { article: article }
      else
        { errors: article.errors.to_a }
      end
    }
  end
end
That’s it, we are done, now we can save new article using addArticle mutation.
1
2
3
4
5
6
7
8
9
10
mutation addArticle{
  addArticle(input: { article: { title: "GraphQL mutation", body: " This article is about graphql mutation using  InputObjectType" } })
  {
    article{
      id
      title
      body
    }
  }
}
If we want to map camel case arguments into DB fields which are in snake case, we can use as keyword.
app/graphql/input_objects/comment_input_object_type.rb
1
2
3
4
5
6
CommentInputObjectType = GraphQL::InputObjectType.define do
  name 'CommentInput'
  input_field :articleId, !types.ID, as: :article_id
  input_field :userId, !types.ID, as: :user_id
  input_field :comment, !types.String
end
This will convert our input params in snake case so that we can assign this to the object without manually converting into snake case.
1
2
inputs[:comment].to_h
=> {"article_id"=>"1", "user_id"=>"1", "comment"=>"New comment"}
You can see sample code here.

Comments