在Apollo + Rails(GraphQL服务器)中创建文件上传的mutation的方法是什么?

目标 (mù


resolve ->(obj, args, ctx) {
  # argsとしてアップロードファイルをそのまま受け取りたい
  YourModel.find(args[:id]).update avatar: args[:file]
}

问题

若要使用GraphQL进行文件上传,不知道应该如何编写mutate。

解决方案 (jiě jué àn)

    1. 在客户端中使用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)
  })
}     
bannerAds