バックエンドとフロントエンドの連携 〜Qiita Nightレポート〜
2023年2月28日、Qiita株式会社オフィスがあるWeWork TK 池田山で、LT大会を行いました。Qiita Nightとは技術についての知見を共有しあうトークイベントで、毎回異なるテーマを設けています。オンラインで開催されることの多いQiita Nightですが、今回はオフラインでの開催。
当日はQiita株式会社プロダクトマネージャーの清野さん、ソフトウェアエンジニアの千葉さん、株式会社Leaner Technologiesでウェブアプリケーションエンジニアをしている大東さんが登壇し、「バックエンドとフロントエンドの連携」のテーマに沿い、それぞれの業務や組織での取り組みを紹介しました。本記事では、当日のLT大会の様子をお届けします。
- QiitaにおけるGraphQLの活用について
- APIプロトコルの一種、GraphQLを使って得られたメリット
- Qiitaの現在の構成と背景
- OpenAPI仕様を守って安全にスキーマ駆動開発する💪
- バックエンドとフロントエンドをどのように連携するか
- REST APIで会話する場合のAPI仕様を知る方法
- OpenAPIでの定義と、バックエンドでの保証
- Committee::Railsを使ってバリデーション
- フロントエンドではaspidaとopenapi2aspidaで保証する
- JSON Schemaを作るPresenterで、Railsアプリケーション (Qiita Team) とフロントエンドの連携をする
- Qiita Team、Qiita、Qiita Jobsはすべて同じRailsアプリケーションとして開発
- RailsからReactをどう利用するか、利用する際の問題は?
- JSON Schema を生成する Presenter を作ってバックエンドとフロントエンドの連携をする
- 懇親会は、和気あいあいと
目次
登壇者プロフィール
目次
- QiitaにおけるGraphQLの活用について
- APIプロトコルの一種、GraphQLを使って得られたメリット
- Qiitaの現在の構成と背景
- OpenAPI仕様を守って安全にスキーマ駆動開発する💪
- バックエンドとフロントエンドをどのように連携するか
- REST APIで会話する場合のAPI仕様を知る方法
- OpenAPIでの定義と、バックエンドでの保証
- Committee::Railsを使ってバリデーション
- フロントエンドではaspidaとopenapi2aspidaで保証する
- JSON Schemaを作るPresenterで、Railsアプリケーション (Qiita Team) とフロントエンドの連携をする
- Qiita Team、Qiita、Qiita Jobsはすべて同じRailsアプリケーションとして開発
- RailsからReactをどう利用するか、利用する際の問題は?
- JSON Schema を生成する Presenter を作ってバックエンドとフロントエンドの連携をする
- 懇親会は、和気あいあいと
QiitaにおけるGraphQLの活用について
最初はQiita開発チームでプロダクトマネージャーをしている清野さんより、アイスブレイクを兼ねた自己紹介からスタートです。「Qiita株式会社でプロダクトマネージャーをしています。バックエンド周りでRailsを書いてきたのでRubyやRailsが好きで、フロントエンドではReactで書くことが多いです。プロダクトマネージャーもしているので、プロダクトマネジメントや設計も好きです」とのこと。続いて本題へ。構成は以下の通りです。
最初はQiitaでのフロントエンドとバックエンドの遷移についての紹介です。「Qiitaはリリースされて10年が経つサービスで、その間にフロントエンド周りの構成も変化してきました。最初のころはjQueryでアニメーションの部分を作っていて、次第にBackbone.jsと言われるjQueryをベースにしたフレームワークを使うようになりました。それらをベースにReactなどでのComponentのUI実装に段々と移行して、hyperappというライブラリの使用を経て、現在はReactとTypeScriptで全体的な実装をしています。」
「対してバックエンドでは、リリース当時からRailsを使い続けていますが、View周りの扱いは変化しています。最初のころはERBメインで書いていて、現在はほぼすべてReactやTypeScriptで書いています」とのことです。
APIプロトコルの一種、GraphQLを使って得られたメリット
続いてはGraphQLについてです。GraphQLはスキーマ、クエリ、リゾルバーという3つの概念で構成されている、APIのプロトコルの一種。GraphQL自体はプロトコルのため各実装はそれぞれの言語でされており、例えばRubyの場合はgraphql-rubyというライブラリを使って開発されています。「分かりやすいように」と、コード例を用いて、GraphQLについての説明に移ります。
「これらのコードはgraphql-rubyというライブラリで書いたサンプルです。fieldなどの定義の部分が先ほどお話したようなスキーマ定義をしている箇所で、fieldと同名のメソッドでリゾルバーでデータのマッピングをしています。①②のコードを書くとスキーマが③のように定義されます。そして定義されている③のようなコードに対して④のクエリを投げると、⑤の結果が出ます。」
「GraphQLを使うメリットは大きく2つあると思っています。1つ目は欲しいデータを過不足なく取得できること。クエリベースで呼び出し元から欲しいデータの指定ができるためです。2つ目はスキーマの型データを用いて定義をするので、GraphQLの各言語の型を自動生成ができること。そうすると、APIで取得した結果の型をフロントエンドへも同期できます。」
Qiitaの現在の構成と背景
そして図を用いながら、Qiitaの現在の構成とその背景、メリットをご紹介。「Qiitaには、ReactのSSRをRailsで行っているという特徴があります。SSR時にはデータを初期値として渡すと思いますが、すべてGraphQL経由でデータを取ってきています。」
この構成になっている背景には、「先ほど話したERBメインだった時代には、素朴なREST APIで実装していました。しかしReactやTypeScriptでフロントエンドを全体的に実装するうちに、様々な課題が顕在化してきました」という事情があったそうです。
「具体的には、バックエンドで書いているAPIの値を変えるときにTypeScriptでの取得結果の型を書き換え忘れてバグる、初期データの取得処理とAPIでの結果のデータ構造が統一化できていなくてバグる、クライアント側での外部APIを呼び出す時のエラーハンドリングの処理が上手にできずクラッシュする」などの課題があったとのこと。
先ほど紹介された構成に変えたことで、大まかに3つのメリットが得られたそうです。「フロントエンドがGraphQLのエンドポイントのみに依存する形になったので、フロントエンド側で考えることを減らせました。TypeScriptの型が自動生成されることで型が一致している状態を担保できるようにもなりました。またSSR時にはGraphQLクエリから経由してデータを取得しているので、バックエンド、フロントエンドで取得するデータのインターフェースも統一できています。」
「懇親会でもぜひお話できたらと思います」との締めくくりで清野さんのLTは終了。続いて、大東さんのセッションです。
OpenAPI仕様を守って安全にスキーマ駆動開発する💪
大東さんも、まずは自己紹介から。「TwitterやGitHubなど、ほとんどのサービスを『@phigasui』というアカウント名で登録しています。今着ているパーカーやスライドに描かれているキャラクターを見かけたら『@phigasui』と思ってもらえたら。現在は株式会社Leaner Technologiesでウェブアプリケーションエンジニアとして働いています。調達購買という、企業が買い物をする業務があるのですが、その業務のスタンダードを刷新して生産性を上げるためのプロダクトを作っています。」
バックエンドとフロントエンドをどのように連携するか
本題に入ります。「LTのコンテンツを作るにあたって僕は楽をしようと、ChatGPTに『Ruby on Rails と Next.js + TypeScript で OpenAPI を用いたスキーマ駆動の開発について説明してください』と投げました。すると答えてくれて、もう僕がLTをするよりもChatGPTに聞けば良いんじゃないかとなりますが、AIに仕事を奪われないよう、しっかり話します」とユーモアも交じえると、聞き手のみなさんからの笑い声も聞こえてきました。
「『どうやってフロントエンドとバックエンドを連携しますか?』とChatGPIに再び聞いてみると、以下の通り。」
「REST APIが多いですね。このRESTを使ってフロントエンド開発をした経験がある人もいるのではないでしょうか。僕たち株式会社Leaner Technologiesで作っているプロダクトも、REST APIで連携しており、Next.jsとTypeScriptのフロントエンドから、RailsのAPIサーバーにリクエストしてレスポンスをもらって動いているSPAのサービスとなっています。」
REST APIで会話する場合のAPI仕様を知る方法
「REST APIで会話する場合、API仕様を知るためにOpenAPI仕様で定義します。OpenAPIを使うと分業しやすくなり、かつ分業しても安全に開発できるようになります。例えばバックエンド側はフロントエンドがどのAPIを使っているかとか、フロントエンド側はバックエンドがどのようにAPIを実装されているかとか、どちらかの仕様を変更するためには互いの仕様を理解している必要があります。しかしOpenAPIでAPIの仕様を定義すれば、お互いにAPIの定義だけをみていれば対応できます。」
OpenAPIでの定義と、バックエンドでの保証
OpenAPI仕様ではHTTP APIの標準的で言語に依存しないインターフェースを定義し、人間とコンピューターの両者が、サービスの能力を発見・理解できるようになるそうです。OpenAPI仕様に従ったOpenAPIドキュメントは、それ自体がJSONで、JSON形式もしくはYAML形式のいずれかで表現される場合がありますが、大東さんいわく「人間には難しい」とのことで、編集はStoplight Studioで行っているそうです。
「OpenAPIで定義できたとして、問題になりそうなバックエンドでAPI仕様の変更に対してどのように保証するかも、ChatGPTに聞いてみました。自動生成するのは難しそうですが、バリデーションツールを使ってテスト実行するのであればできそうですよね。」
Committee::Railsを使ってバリデーション
「こちらではCommittee::Railsを使っています。Committee::Railsでは、Rails向けにリクエストやレスポンスのインターフェースのバリデーターを提供してくれている、OpenAPIのエコシステムです。Committee::Railsを使ってバリデーションするだけで、OpenAPIを定義したJSONと、パスに対するリクエストのレスポンスがきちんとスキーマにマッチしているかどうかを検証できるので、APIの仕様変更に追従できていると保証できます。バックエンドは以上です。」
フロントエンドではaspidaとopenapi2aspidaで保証する
続いてはフロントエンドについて。「Next.jsでTypeScriptを開発しているため、型を使って保証できればいいなと考え、aspidaとopenapi2aspidaを使っています。aspidaはTypeScriptフレンドリーなHTTP クライアントラッパーです。aspidaだけを使うと、パスとそれに対するレスポンスの型や定義をすべて書かなければいけないのですが、openapi2aspidaを使うと先ほどのOpen APIのJSONでの定義からaspidaの型定義ファイルを出力できます。」
「するとaspidaを使ってリクエストしたいAPIエンドポイントの補完がききますし、パラメーターの型もチェックしてくれます。さらにはレスポンスのボディに対しても型が付きます。」
最後にAPIなしでのフロントエンドの開発については「APIをモックすることでバックエンドなしで実装できる」そうで、「Mock Service Workerというライブラリを使ってモックしているのですが、『このエンドポイントに対してはこういうレスポンスを返す』というような定義をしています」とのことです。続いて最後のセッションに移ります。
JSON Schemaを作るPresenterで、Railsアプリケーション (Qiita Team) とフロントエンドの連携をする
LT登壇3人目は、Qiita株式会社でソフトエンジニアをしている千葉さんです。「新卒でQiita株式会社へ入り、一度違う会社へ行き、最近戻ってきました。今は各アプリケーション(Qiita、Qiita Team、Qiita Jobs)の開発や、開発体験者向上などをしています。好きな言語はRubyで、最近の趣味はデバイスを買って散財することです。」
タイトルは「Railsアプリケーションとフロントエンド間の連携を行う Presenter を実装して、幸せな開発体験を提供した話」。QiitaTeamとQiita Jobsでの取り組みについての話をメインに展開します。
Qiita Team、Qiita、Qiita Jobsはすべて同じRailsアプリケーションとして開発
導入として、サービスの紹介から。「QiitaTeamは、特定の会社や組織で技術に関するナレッジを共有するためのSaaSです。サービス提供がスタートしてから今年で10年が経ちます。Qiita株式会社ではQiita TeamのほかにQiitaとQiita Jobsがありますが、すべて同じRailsアプリケーションとして開発されています。」
ここ1年ほど、Qiita Teamではフロントエンドのアップデートを行っているそうです。「もともとQiita TeamはQiitaの延長線上のようなUIだったのですが、企業向けのサービスゆえ様々な記事が投稿されるので、より見やすく探しやすくなってほしいという意見がありました。その要望に答えるため、デザインのアップデートをしています」とのこと。
「Qiita TeamではViewの組み立てをRails(Slim)でしているのですが、TypeScript(React)でViewを組み立てる方法へ移行している最中です。ViewをReactに寄せることで開発体験の向上を狙っています。今でもQiita TeamですとSlimとReactのページが混ざっていて、両者を行ったり来たりしないといけません。Reactで統一すればスイッチングコストがかからないのに加え、デザインを CSS-in-JS(Emotion)に寄せられるというメリットがあります。またQiitaではフロントエンドをReactで書いていたので、Qiitaでのフロントエンド資産をQiita Teamで利用できるという良さもありました。」
RailsからReactをどう利用するか、利用する際の問題は?
RailsからReactを利用する方法についてです。「Qiita Team では、React on Rails を利用しています。使用感をざっくり言うと、ReactのComponentをRailsのView(Slim)にマウントするような感じです。React on Rails がフロントエンド側に必要なデータの埋め込みや、それを利用した React コンポーネントの Render を行うまで自動でやってくれます。 」
「使い始めるまでは非常に簡単です。ただ、実際に開発を続けていく上では大変なこともあります。まず1つはテストの問題。例えばModelとControllerをRailsで実装して、ReactでViewを行うとなると、2つの言語を挟んでいるのでテストが大掛かりになります。SSRしない場合は大掛かりなテストが必要になるのも問題で、ブラウザテスト(System Testなど)をしないと、そもそも渡しているデータがおかしいとか、Componentの指定がおかしいとか。RailsとReactの間の問題もチェックできません。」
「加えてバックエンドからもらったデータとReactの定義がきちんと合っているかの保証がないので、フロントエンドの型を独自に書くのも大変です。型を書いたとしても、本当に合っているのかとか。フロントエンドに渡すデータを作る場所も考えないといけません。Qiitaは長く続いているサービスということもあり、このような実装をどのように、どこに書くかを統一しきれていない部分もありました。 React on Rails は便利ですが、バックエンドとフロントエンドを上手く連携するには心もとない部分も多いです。」
JSON Schema を生成する Presenter を作ってバックエンドとフロントエンドの連携をする
では、どのようにバックエンドとフロントエンドを連携するか。よく使われる手法は2つあるそうです。「1つ目はViewとそれ以外、例えばModel Controllerを繋ぐPresenter層を置くこと。Presenter は、ViewModelやSerializerとも呼ばれます。Rails での代表的な Presenter には、 ActiveModelSerializers や JBuilder、Alba などがあります。もう1つはスキーマを作ること。異なる2つのシステムの間でインターフェースをスキーマとして定義することで、それぞれで信用できる型を作って検証やテストができるようになります。」
「この2つを上手く組み合わせることで、先ほど挙げた3つの問題『①統合テストを作るのが難しく大掛かりになること、②バックエンドとフロントエンド間で受け渡すデータをどのようにして作るかが問題になること、③型が信用できないこと』を解消できるのでは? と考え、JSON Schema を生成できるPresenterを作りました。バックエンドからフロントエンドへはReact on Railsでデータを渡しているのですが、間にJSON Schema を生成する Presenter を書くことでデータを組み立てる場所についての問題が解決しますし、それぞれで検証できます。」
このアプローチのもと実装したライブラリは、オープンソースとして、 GitHub上で公開されています。
「生成されるJSON Schemaはこのように書かれています。フロントエンドでJSON Schemaを使うとTypeScriptを生成できて、そのようにして生成された型定義をフロントエンドのComponentに使います。生成された型定義を使っているので、TypeScript側では型を信用して実装することができます。バックエンドでは、JSON Schemaを使って渡しているデータが正しいかをチェックできるので、フロントエンドとバックエンドそれぞれが独立して検証を行えます。また Presenter レイヤーを明確に導入したことで Rails 側の秩序が生まれたり、効率的なクエリを発行しやすくなったりという利点もあると、実際にやってみて分かりました。」
千葉さんがQiitaに投稿した記事では、図やコードを用いて詳しく紹介されています。そちらもぜひご覧ください。
JSON Schema を作る Presenter で Rails アプリケーション (Qiita Team) とフロントエンドの連携をする by @tomoasleep
懇親会は、和気あいあいと
LT後に行われた懇親会では「Qiita に出戻りした理由は?」とキャリアについての質問が飛び交ったり、「他社と、それぞれの会社でのバックエンドとフロントエンドの連携やその課題」というテーマで意見交換がされたり。和気あいあいとした雰囲気ながら、情報を共有し合える場となりました。
今後もQiita Nightでは、様々なテーマで技術についての知見を共有していきます。過去にはPHP開発について幅広いテーマでLTを実施したり、AWSを活用する様々なシーンで活躍されているエンジニアを招いて、AWSをテーマに語り尽くしていただいたりしました。イベント情報はconnpassに掲載しているほか、Qiitaの公式イベントページや毎週水曜日にお送りするメールマガジンでも発信しています。興味がある方はチェックしてみてください。