はじめに
graphql-rubyでファイルをアップロードするにはBase64にエンコードしてGraphQL側に渡す必要があります。 しかし、この方法だとファイルサイズが3割程度大きくなり下記のデメリットが発生します。 - 通信が遅くなる - ファイルサイズによってリソースを使う
そこで、マルチパート形式でファイルをアップロードするためのgemがあり、それを実装してみたので紹介します。
記事について
graphql-ruby初学者向けの記事を書いています。
関連記事
- graphql-batchでN+1を解消してみた
- 無限ページネーション
- 個人開発のcodegen.ymlの設定について考えてみた
- ファイルアップロード機能を実装してみた(現在の記事)
使用技術
ruby 3.1.2 rails 7.0.3.1 graphql 2.0.13 react 18.2.0 apollo client 3.6.9
モデル
class Task < ApplicationRecord has_one_attached :image end
フロントエンド
公式の通り、apollo-upload-client
を導入して実装していきます。
https://www.apollographql.com/docs/react/data/file-uploads/
yarn add apollo-upload-client or npm install apollo-upload-client
const link = createUploadLink({ uri: "/graphql", }); const client = new ApolloClient({ // 省略 link: Link, });
バックエンド
gemを用いてマルチパート形式でのアップロードを実装していきます。 https://github.com/jetruby/apollo_upload_server-ruby
gem 'apollo_upload_server'
module ObjectTypes class File < Types::BaseScalar description '画像' def self.coerce_input(file, _context) ActionDispatch::Http::UploadedFile.new( filename: file.original_filename, type: file.content_type, headers: file.headers, tempfile: file.tempfile ) end end end
# 追記 argument :image, ObjectTypes::File, required: false
def resolve(params:) task = Task.new(params.to_h) if task.save { task: } else raise GraphQL::ExecutionError, task.errors.full_messages.join("\n") end end
今回の実装では新しくFile型を定義することで実装しました。
ActionDispatch::Http::UploadedFile.new
でインスタンスを生成していますが、attributesはこちらを参照してください。https://api.rubyonrails.org/v7.0.4/classes/ActionDispatch/Http/UploadedFile.html
gemのREADMEにあるようにApolloUploadServer::Upload
という型を用いることもできます。
しかし、下記のようにmutation側に一手間加える必要があります。
task.image.attach(io: params.image.to_io, filename: params.image.original_filename)
他のmutationでファイルアップロードが必要になった際、毎回この記述をするのは手間なので、今回の実装方法がベターなのでは無いかと思いました。
会社の紹介
株式会社 mofmof では一緒に働いてくれるエンジニアを募集しています。 興味のある方は是非こちらのページよりお越しください! https://www.mof-mof.co.jp/ https://www.mof-mof.co.jp/recruit/