在Apollo + Rails(GraphQL服务器)中创建文件上传的mutation的方法是什么?
目标 (mù
resolve ->(obj, args, ctx) {
# argsとしてアップロードファイルをそのまま受け取りたい
YourModel.find(args[:id]).update avatar: args[:file]
}
问题
若要使用GraphQL进行文件上传,不知道应该如何编写mutate。
解决方案 (jiě jué àn)
-
- 在客户端中使用https://github.com/jaydenseric/apollo-upload-client
使用https://github.com/rmosolgo/graphql-ruby
准备传递给GraphqlController等的execute(参考)的参数
创建File标量
创建将File标量作为input_field的Mutation
编写用于客户端上传的mutate
1 阿波罗上传客户端
使用yarn添加apollo-upload-client
用’apollo-upload-client’引入createNetworkInterface方法,并将其替换为常规的createNetworkInterface。
2 GraphQL-Ruby 二GraphQL-Ruby
gem "graphql"
将参数传递给GraphqlController等中的执行函数execute。
https://rmosolgo.github.io/graphql-ruby/schema/generators的API提供了一种生成GraphqlController的方法。
使用命令rails generate graphql:install可以生成GraphqlController,但该控制器的数据格式与来自apollo-upload-client发送的数据格式不同。
在文件上传时,由于增加了名为”operations”的参数,因此需要使用它。
def execute
context = {
# Query context goes here, for example:
# current_user: current_user,
}
if params[:operations].present?
# この部分で、必要となる query と variables を設定する
operations = ensure_hash(params[:operations])
variables = {
"input" => operations[:variables].
merge({"file" => params["variables.file"]})
}
query = operations[:query]
else
variables = ensure_hash(params[:variables])
query = params[:query]
end
result = RailsAppNameSchema.execute(query, variables: variables, context: context)
render json: result
end
4个文件的标量
# autoload_pathsを使っても良いけど、フォルダとそれに対応するようにmoduleを
# 作っていけばautoload_pathsを設定しなくても自動に読み込んでくれる
# https://railsguides.jp/autoloading_and_reloading_constants.html
module Types
module Scalars
FileType = GraphQL::ScalarType.define do
name "File"
description "ActionDispatch::Http::UploadedFile"
coerce_input ->(action_dispatch_uploaded_file, ctx) {
# graphql_controller.rb で渡した params["variables.file"] は
# Railsで普通の ActionDispatch::Http::UploadedFile
# http://api.rubyonrails.org/classes/ActionDispatch/Http/UploadedFile.html
action_dispatch_uploaded_file
}
end
end
end
五变异。
# ここも同じでautoload_pathsを使わなくても、フォルダとmoduleを対応させて作っている
module Mutations
module YourModel
# ここではEditとした
Edit = GraphQL::Relay::Mutation.define do
name "YourModelEdit"
input_field :id, !types.ID
# moduleで階層を作っているので
# autoload_pathsを使ってるのであれば
# 参照できる定数名で指定
input_field :file, Types::Scalars::FileType
return_field :results, types.Boolean
resolve ->(obj, args, ctx) {
YourModel.find(args[:id]).update avatar: args[:file]
{results: true}
}
end
end
end
6 进行上传的突变
// ここで言うtargetは<input type="file">の実体
// <input type="file" ref={ (input) => this.fileInput } />
// などで参照できるようにしておいて
// this.fileInput.files[0] を渡すなどする
this.props.mutate({
variables: {
id,
avatar: target.files[0]
}
}).then(({data}) => {
console.log(data)
})
}