もふもふ技術部

IT技術系mofmofメディア

ファイルアップロード機能を実装してみた(graphql-ruby+ActiveStorage+React)

はじめに

graphql-rubyでファイルをアップロードするにはBase64エンコードしてGraphQL側に渡す必要があります。 しかし、この方法だとファイルサイズが3割程度大きくなり下記のデメリットが発生します。 - 通信が遅くなる - ファイルサイズによってリソースを使う

そこで、マルチパート形式でファイルをアップロードするためのgemがあり、それを実装してみたので紹介します。

記事について

graphql-ruby初学者向けの記事を書いています。

関連記事

  1. graphql-batchでN+1を解消してみた
  2. 無限ページネーション
  3. 個人開発のcodegen.ymlの設定について考えてみた
  4. ファイルアップロード機能を実装してみた(現在の記事)

使用技術

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/