LoginSignup
19

More than 5 years have passed since last update.

apollo + rails(graphqlサーバー)でファイルをアップロードするmutationを作る方法

Last updated at Posted at 2017-05-05

目標

mutation.rb

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

問題

graphqlを使ってファイルアップロードをする時、どのようにmutateを書けば良いのか分からない

解決方法

  1. https://github.com/jaydenseric/apollo-upload-client をクライアント側で使う
  2. https://github.com/rmosolgo/graphql-ruby を使う
  3. GraphqlController等でのexecute(参照)に渡すパラーメーターを準備する
  4. Fileスカラーを作る
  5. Fileスカラーをinput_fieldとするMutationを作る
  6. クライアント側のアップロードをするmutateを書く

1 apollo-upload-client

yarn add apollo-upload-client

import {createNetworkInterface} from 'apollo-upload-client'
で、通常のcreateNetworkInterfaceと入れ替える

2 graphql-ruby

Gemfile
gem "graphql"

3 GraphqlController等でのexecuteに渡すパラメーター

https://rmosolgo.github.io/graphql-ruby/schema/generators
rails generate graphql:install
で作られた状態のままのGraphqlControllerではapollo-upload-clientから送られるデータ形式と違う

ファイルアップロード時はoperationsと言うパラメーターが増えているのでこれを使う

graphql_controller.rb
  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 Fileスカラー

app/graphql/types/scalars/file_type.rb

# 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

5 Mutation

app/graphql/mutations/your_model/edit.rb

# ここも同じで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 アップロードをするmutate

// ここで言う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)
  })
}     

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19