esm アジャイル事業部 開発者ブログ

永和システムマネジメントアジャイル事業部の開発者ブログです。

Rails / OSS パッチ会 2019年2月のお知らせ

コミュニティマネージャの koic です。

Rails / OSS パッチ会を 2月28日(木)に開催します。

この会をひとことでいうと、日頃のお仕事で使っている Rails をはじめとする OSS への upstream にパッチを送る会です。

会には RubyRails のコミッターである顧問の a_matsuda もいますので、例えば Rails に送るパッチのネタがあるけれど、パッチを送るに適しているかの判断やパッチを送る流れが悩ましいときなど a_matsuda に相談して足がかりにするなどできます。

前回 2019年1月のパッチ会では、Rails 6 に向けた RDBMS ごとの処理系差異の議論など交えつつ盛り上がったりしました。

f:id:koic:20190128134351j:plain:w400

以下、前回の活動が関わる成果です。パッチに向けた設計議論が多めだったため会の時間帯で観測されたパッチは少なめです (私の見落としがいくつかありそう) 。

chiastolite: Rails

github.com

colorbox: Danger

github.com

colorbox: Rails

github.com

kamipo: Rails

github.com

開催時間は 17:00-19:00 となりますがご都合のあう方はぜひご参加下さい。特に募集ページなど設けませんので、興味のある方は永和システムマネジメントの神田オフィスまでお越し下さい。

agile.esm.co.jp

Rails 6 Beta が出ていることもあり、その動きに向けたディスカッションや、終わった後は有志で懇親会などあるかもしれません。

f:id:koic:20190128135143j:plain:w400

GraphQL でファイルをアップロードしたい

こんにちは、hibariya です。さいきん GraphQL でのファイルアップロード方法について知りたいなと思いちょっと検索してみたところ、すぐにはこれといった方法に辿りつけなかったので気になって調べました。手元で試した感じだと GraphQL multipart request specification という仕様が良さそうでしたので、今日はそのことについて紹介したいと思います。

GraphQL でのファイルアップロードはめんどう

現在のところ、GraphQL の仕様ではファイルアップロード方法については特に規定されていないため、自分たちで方法を決めて実装する必要があります。が、これは少し面倒です。HTTP で GraphQL を使う場合、サーバに渡す値はたいてい JSON のようなフォーマットでシリアライズしますが、FileオブジェクトはそのままJSONにはエンコードできません。

{
  "query": "mutation CreateMessage($input: CreateMessageInput!) { ... }",
  "variables": {"input": {"message": "hi", "file": "<アップロードしたい File をここに...??>"}}
}

ですから、GraphQL でファイルをアップロードしようというときには少し工夫が必要になります。方法としてよく挙げられているのは以下の3つでしょうか。

  1. ファイルを Base64 エンコードして JSON に含める
  2. ファイルのアップロードには GraphQL とは別の API を使う
  3. リクエストを multipart/form-data として送る

最初の方法1は、そのままではシリアライズできないファイルを Base64 エンコードすることで JSON として扱えるようにする方法です。クライアントとサーバ間の通信方法を大きく変える必要がなさそうで、シンプルそうに見えます。ただ、巨大なファイルを扱う場合には、あまり考えずにサーバ側のアプリケーションを実装すると巨大な JSON をオンメモリで扱うことになって面倒そうな気がします *1。あとはファイルサイズが若干増えますし、ファイル名のようなメタ情報を自分で渡さないといけないのも少し面倒ですね。

次の2はファイルアップロードを GraphQL API では扱わず、別途用意した REST API などを併用するという方法です。確かに方法のひとつとしては考えられますが、使い方によっては、元々ひとつだったトランザクションを分ける必要が出てくるなど用途を選びそうです。

最後の3は GraphQL のリクエストを multipart/form-data として送る方法です。ここで紹介する GraphQL multipart request specification は、この方法を採用しています。

GraphQL multipart request specification

リクエストに multipart/form-data を使う方法であれば、巨大なファイルは (例えば Ruby なら) Rack のレイヤで少しずつファイルに書き出される *2 ため、サーバサイドのメモリ使用量についての心配はひとまず置いておけます。また、この方法にはいくらか認知されている仕様として、GraphQL multipart request specification というものがあります。

仕様が定めているのは、multipart でどうやって GraphQL リクエストを送るのか、特に GraphQL の変数の値としてとしてどうやってファイルをマッピングするのかというところです。仕様そのものは単純なので、例をまじえて説明します。 README とほぼ同じですが、冒頭の例で説明するとクライアントは以下のような感じの multipart message をサーバに送ることになります。

--------------------------cec8e8123c05ba25
Content-Disposition: form-data; name="operations"

{
  "query": "mutation CreateMessage($input: CreateMessageInput!) { ... }",
  "variables": {"input": {"message": "hi", "file": null}}
}

--------------------------cec8e8123c05ba25
Content-Disposition: form-data; name="map"

{ "0": ["variables.file"] }

--------------------------cec8e8123c05ba25
Content-Disposition: form-data; name="0"; filename="hi.png"
Content-Type: image/png
Content-Transfer-Encoding: base64

(Base64 エンコード済みのファイル)

--------------------------cec8e8123c05ba25--

最初の part を見ると、operations というパラメータの中に GraphQL のクエリや変数が格納されています。ここは相変わらず JSON ですね。ところで肝心の変数 file (variables.input.file) が null となっています。これはtypo ではなくそういうもので、ファイル本体は別の part となっているため、サーバ側で適切にマッピングして、この null を上書きする必要があります。この例では、ファイルは最後の part にある hi.png です。真ん中には map というパラメータの part があります。これは、最初に登場した null と実際のファイルをマッピングするための情報です。この例の { "0": ["variables.file"] } でいうと「0 というパラメータは、variables.file の中身ですよ」といった意味になります。

サーバ側は、この仕様の実装がアプリケーションより下の層にあれば、アプリケーションを大きく変更することなくファイルのアップロードを実現できます。Ruby なら Rack のミドルウェアとして実装して、ファイルを params[:operations][:variables][:input][:file] で参照できるようにする、という具合です。

クライアント側も、変更が必要なのは主にネットワークレイヤになります。実装のひとつに Apollo Link のミドルウェアとしてこの機能を提供しているものがあるので、もし Apollo を使っているならそれを導入することで対応できるでしょう。残念ながら現時点では Relay (Modern) の実装として公開されているものは見当らないのですが、Relay の Network Layer をカスタマイズすることでなんとかなりそうです。Relay の方はちゃんと試してはいないので、知見をお持ちの方がいれば教えてください。

良さと注意点

現時点だと GraphQL でファイルをアップロードする方法としてはこの GraphQL multipart request specification が良さそうだと感じています。理由としては、まず仕様として公開されていて、既にいくつか実装があるということ。また、それらの実装を使うことで既存のアプリケーションの実装を大きく変えなくてもよいというのも良いですね。様々なクライアントに対応できるのが GraphQL の良さのひとつですので、Apollo/Relay どちらからでも使えるというのも重要なポイントだと思います。それから、私はよく Ruby を使うので Ruby の実装があるのも嬉しい。

注意が必要な点としては、あくまで multipart/form-data を前提にしているので、それに対応できないサーバやクライアントでは採用できません。

Ruby でのサーバ側実装

サーバサイドではどんな感じで導入すれば良いのでしょう。最後に GraphQL multipart request specification の Ruby のサーバサイド実装として jetruby/apollo_upload_server-ruby を紹介します。この gem が提供するのは次の2つです。

  • アップロードファイルを表現する Upload というスカラ型
  • GraphQL multipart request specification を実装した Rack middleware

Gemfile に足すと middleware が挿入されるので、基本的にはそれで導入は完了です。ただ、ひとつ注意点があります。このミドルウェアはファイルを ApolloUploadServer::Wrappers::UploadedFile として params に入れてくれるのですが、これは ActionDispatch::Http::UploadedFileDelegateClass でラップしたオブジェクトです。そのため、Active Storage にそのまま渡せないという問題があります (jetruby/apollo_upload_server-ruby#10)。私が手元で検証した際は、workaround として この comment のように IO とファイル名をひとつずつ渡しました。

おわりに

GraphQL でのファイルアップロードについて現状の私の理解を書きました。ここで紹介した GraphQL multipart request specification は、良さそうとは言いつつも、本格的な導入はまだ残念ながらできていません。知見をお持ちの方がいればぜひ教えてください。

もうすぐ大晦日ですね。よいお年を!

参考

Rails / OSS パッチ会 2019年1月のお知らせ

コミュニティマネージャの koic です。

新年最初の Rails / OSS パッチ会を2019年1月24日(木)に開催します。

この会をひとことでいうと、日頃のお仕事で使っている Rails をはじめとする OSS への upstream にパッチを送る会です。

会には RubyRails のコミッターである顧問の a_matsuda もいますので、例えば Rails に送るパッチのネタがあるけれど、パッチを送るに適しているかの判断やパッチを送る流れが悩ましいときなど a_matsuda に相談して足がかりにするなどできます。

前回 2018年12月のパッチ会には、a_matsuda が元 Rails コミッターで来日中の senny を連れてきてくれて、初の海外ゲストを迎えた会になりました。

f:id:koic:20181217150924j:plain:w400

以下、前回の活動が関わる成果です。

amatsuda: ghq_transfer

github.com

kamipo: Rails

github.com

koic: RuboCop

github.com

meganemura: OpenAPI Generator

github.com

sue445: RuboCop

github.com

unasuke: Itamae

github.com

unasuke: Rails

github.com

yahonda: Rails

github.com

開催時間は 17:00-19:00 となりますがご都合のあう方はぜひご参加下さい。特に募集ページなど設けませんので、興味のある方は永和システムマネジメントの神田オフィスまでお越し下さい。

agile.esm.co.jp

Ruby 2.6.0 リリース後の新年最初のパッチ会です。終わった後は有志で新年会などあるかもしれません。

Rails Developers Meetup 2018 Day 4 に colorbox と junk0612 が登壇、ランチスポンサーをします

2018年12月8日(土) に開催される Rails Developers Meetup 2018 Day 4 Nouvelle Vague に colorboxjunk0612 が登壇し、ランチスポンサーとして協賛します。

techplay.jp

junk0612 こと小林 純一の講演 (11:30-11:50 / トラックB)

junk0612 こと小林 純一の講演は 11:30 - 11:50 の枠で『Rails x パターン』というタイトルで、Rails プロジェクトにおけるソフトウェアデザインなどのパターンについて取り上げた話です。

新卒入社から3年目にあたり、これまで経験したいくつかの Rails プロジェクトで得たソフトウェアパターンの知見を共有した話になるようです。

スポンサーランチ (13:15-14:00 / トラックA)

数量限定ではありますが、ランチスポンサーとしてお弁当を用意します。

ランチをとりながら、弊社が開発しているビデオチャットサービス Linkup の紹介をします。

linkup.world

colorbox の講演 (14:35-14:55 / トラックA)

colorbox の講演は 14:35 - 14:55 の枠で『Kata の作り方』というタイトルで、Dave "達人" Thomas の CodeKata にインスパイアされた Kata の話をします。

codekata.com

Ruby 3.0 で話題に取り上げられている型 (Type) ではなく、武道や舞踊などの型 (Kata) をソフトウェア開発の流儀に型どっての話になるようです。

当日を楽しみに会場でお会いしましょう。

f:id:koic:20181130114625p:plain:w300


永和システムマネジメント アジャイル事業部では、Ruby / Rails を使ったアジャイルなソフトウェア開発を一緒にしていくメンバーを募集しています。

www.wantedly.com

新メンバーの入社歓迎会を行いました!

フィヨルド様のブログで既報の通り、ブートキャンプからの新しく入社してくれた @yoshinotaiki (通称「神」)の入社歓迎会を行いました。 神の新しい門出を祝うために、フィヨルド様から駒形さん、町田さんにも出席いただいて、楽しい時間になりました。

f:id:m_pixy:20181122154906j:plain 神による入社の挨拶。

f:id:m_pixy:20181122154951j:plain 駒形さんからの祝福の言葉。

f:id:m_pixy:20181122155046j:plain 事業部長の挨拶を聞かずにカメラ目線の神。

楽しい歓迎会をまたやりたいので、一緒に仕事をしてくれる仲間を募集しています。興味のある方はこちらをご覧ください。

f:id:m_pixy:20181122155244j:plain

Rails / OSS パッチ会 2018年12月のお知らせ

コミュニティマネージャの koic です。

今年最後の Rails / OSS パッチ会を2018年12月14日(金)に開催します。

この会をひとことでいうと、日頃のお仕事で使っている Rails をはじめとする OSS への upstream にパッチを送る会です。

会には RubyRails のコミッターである顧問の a_matsuda もいますので、例えば Rails に送るパッチのネタがあるけれど、パッチを送るに適しているかの判断やパッチを送る流れが悩ましいときなど a_matsuda に相談して足がかりにするなどできます。

前回の活動が関わる成果は以下でした。

kamipo: Rails

github.com

koic: Psych

github.com

koic: RuboCop

github.com

sue445: Itamae

github.com

y-yagi: Rails

github.com

github.com

yoshino: Danger

github.com

開催時間は 17:00-19:00 となりますがご都合のあう方はぜひご参加下さい。特に募集ページなど設けませんので、興味のある方は永和システムマネジメントの神田オフィスまでお越し下さい。

agile.esm.co.jp

Ruby 2.6.0 リリース前の今年最後のパッチ会です。終わった後は有志で忘年会などあるかもしれません。

f:id:koic:20181123163935j:plain:w400