使用GraphQL的mutation时,将对象作为参数传递

在使用graphql-ruby编写Mutation时,遇到了当向参数传递对象而不是标量类型时需要使用variables的情况,因此在这里留下备忘录。

环境

Note: This is the native Chinese translation for “environment.”

    • Rails 6.0.0

 

    graphql-ruby 1.9.14

翻译为中文:
目标

    • createUser という mutation の argument にオブジェクトを渡す

 

    クライアント側のクエリでは variables を用いる

进行步骤

在MutationType中新增一个字段。

module Types
  class MutationType < Types::BaseObject
    field :createUser, mutation: Mutations::CreateUserMutation
  end
end
    mutation_type.rb に createUser というフィールドを追加し、 Mutations::CreateUserMutation を紐付ける

实现 CreateUserMutation

module Mutations
  class CreateUserMutation < GraphQL::Schema::RelayClassicMutation
    graphql_name "CreateUser"

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

    argument :user, Types::Attributes::UserInput, required: true

    def resolve(user:)
      created_user = User.create(user.to_h.indifferent_access.transform_keys(&:underscore))

      { user: created_user }
    rescue ActiveRecord::RecordInvalid => error
      { error: error.message }
    end
  end
end

field :user は、この mutation 実行後のレスポンスとして取得できるフィールド。

Types::UserType は普段 query で使用する BaseObject なので内容は割愛。

argument :user が本題。 user というリクエストパラメータを受け取ることを宣言。そのオブジェクトの方を Types::Attributes::UserInput というクラスで定義している(詳細は後述)。
上記で宣言したリクエストパラメータを resolve メソッドのキーワード引数で受け取る。

顺便说一句,用户的内容不是哈希对象,而是Types::Attributes::UserInput的实例。
因此,有以下两种方法来访问其中的属性。

user.postal_code # インスタンスメソッドを通してアクセス
user[:postalCode] # ハッシュのキーを通してアクセス(この時、プロパティ名はキャメルケースにする)

只是执行to_h是不够的,因为键仍然是驼峰命名法,所以需要使用with_indifferent_access.transform_keys(&:underscore)这个冗长的转换步骤。
显然,预计还会在其他变异中使用到,所以我认为在Types::BaseInputObject类中定义一个实例方法是很好的解决办法。

module Types
  class BaseInputObject < GraphQL::Schema::InputObject
    argument_class Types::BaseArgument

    def to_params
      to_h.with_indifferent_access.transform_keys(&:underscore)
    end
  end
end

通过将`user.to_h.indifferent_access.transform_keys(&:underscore)`替换为`user.to_params`,可以实现这一点。

实现 Types::Attributes::UserInput

class Types::Attributes::UserInput < Types::BaseInputObject
  argument :name, String, required: true
  argument :gender, Integer, required: true
  argument :profile, String, required: true
  argument :postal_code, String, required: true
end
    • 先述した resolve メソッドで受け取るキーワード引数の型を下記のように定義する。型の書き方などは query_types 等と同様。

 

    さらにオブジェクトをネストしたい時は Types::BaseInputObject を継承する別のクラスを指定するのかな?(試してない)

以上是服务器端的实现已完成。

编写从客户端发送的查询

mutation registerUser(
  $user: UserInput!
) {
  createUser(input: { user: $user }) {
    user { id postalCode profile }
  }
}
    • 1行目の registerUser はこのクエリに付けた適当な名前なので変えても動く。

 

    • 2行目の UserInput! の ! がキモ。 Mutations::CreateUserMutation の argument で required: true を指定した場合、この ! を付けないと Nullability mismatch on variable $user というエラーが出る(ここで1時間くらいハマった)。

 

    • 4行目の createUser が Types::MutationType で定義した mutation 名。

 

    5行目の user { id postalCode profile } が mutation 実行後のレスポンスボディに入れてほしいオブジェクトとフィールドの指定。

执行查询

query = <<-QUERY
  mutation registerUser(
    $user: UserInput!
  ) {
    createUser(input: { user: $user }) {
      user { id postalCode profile }
    }
  }
QUERY
variables = {
  karte: {
    name: "midwhite",
    gender: 1,
    profile: "I am a software engineer."
    postalCode: "000-0000"
  }
}
AppSchema.execute(query, variables: variables).to_h
    variables に user オブジェクトとして渡したいパラメータをハッシュで記述する。

我想实际上是从前端使用 Apollo 发送查询和变量,只要知道查询的格式,就不会特别困惑,所以省略了这部分。只要到了这一步,就可以写出可实际应用的变异了。我想现在是时候尝试用仅限于 GraphQL 的端点来实现应用程序了。

广告
将在 10 秒后关闭
bannerAds