使用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 的端点来实现应用程序了。