はじめに
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/