Redwood.jsとは
React-GraphQL-Prismaで構成されたフルスタックフレームワークです。
下記のコマンドを打つだけで、フロント、バックエンド、テスト、Storybook環境を構築してくれます。
今回はそのコード生成についてまとめてみました。
Prisma、GraphQLスキーマからの自動生成
Redwood.jsはPrismaとGraphQLのSDLからコードを生成する仕組みが非常に強力です。
どのような生成ができるか下記で説明します。
コード生成の種類
Scaffold
# scaffoldの生成コマンド
yarn rw g scaffold ModelName
Ruby on RailsのScaffoldと同様に、フロントエンドとバックエンドのコードを一度に生成します。
後述するSDL, Service, Page, Cell, Componentで生成されるファイルをまとめて生成します。
scaffoldで画面を生成するときには基本的なCRUD操作は全てカバーされており、一覧、詳細、作成、編集のページが一度に生成されます。また、共通のレイアウトに削除ボタンも実装されています。
例えば、以下のようなPrismaのスキーマがあった場合、
model Post {
id Int @id @default(autoincrement())
title String
body String
}
これにより、以下のPathにそれぞれページが生成されています。
- newPost(作成) -> /posts/new
- editPost(編集) -> /posts/{id:Int}/edit
- post(詳細) -> /posts/{id:Int}
- posts(一覧) -> /posts
管理画面ライクなUIでデータの作成、編集、閲覧が行えるので、POC目的であればデータモデリングとScaffoldのみである程度は目標が達成できます。
早く動く状態を作るという意味では、他のフレームワークでは出せないスピードだと思います。
ただし、注意点として、フロントエンドのStorybookやテストは生成されません。
Scaffoldの使いどころ
基本的なCRUD操作が全てカバーされるため、とりあえず動くものが作りたいという時には、適切な方法です。
ただし、テストが生成されないので、開発が本格的に始まる前に別のコマンドを実行するか、自分でStorybookとテストを実装する必要があります。
SDL
# 実際のコマンド
yarn rw g sdl ModelName
Redwood.jsのSDLはGraphQLのSDL(スキーマ)と対応しています。
SDLの生成コマンドはGraphQLのSDLとそれに対応するService(GraphQLの resolver)を生成します。
Prismaの型から基本的なCRUD操作のQueryとMutationを生成してくれます。
Scaffoldとは異なり、こちらはテストのファイルを生成します。
SDLの生成はprismaのスキーマから行われるため、Prismaのスキーマ定義がないクエリについては生成することができません。
また、queryのみの場合など、必要な定義が少ない場合は自動生成ではなく、手書きで定義する方が早いこともあります。
その場合は--no-crud
オプションをつけることでMutationを除き、Queryのみ生成することができます。
SDLの生成コマンドの使いどころ
Scaffoldと異なり、テストコードが生成されるため、実際にコミットするコードについてはSDLコマンドで生成することになります。
後述のPage, Cell, Componentの生成については、このSDLがないと生成できないため、それらの生成の前段で使うことになります。
Service
# Serviceの生成コマンド
yarn rw g service ModelName
ServiceはGraphQLのresolverを定義する部分です。Redwood.jsの公式ドキュメントでは
Redwood aims to put all your business logic in one place—services.
とあるように、ビジネスロジックは全てこのService内に格納することを想定しています。
Serviceの生成コマンドはGraphQLのスキーマであるSDLから必要なServiceのコードを生成します。
Serviceの生成コマンドの使いどころ
基本的にSDLの生成コマンドがServiceを生成するため、このコマンドの出番はほとんどないかと思います。
ただし、Prisma定義のあるモデルのSDLを手書きで実装した場合は、こちらのコマンドから、Serviceを生成することができます。
Page
# Pageの生成コマンド
yarn rw g page ModelName
PageはフロントエンドのコンポーネントでNext.jsのページと同様に、routerから参照されるコンポーネントです。
Page生成コマンドでは、SDLを参照することはないため、バックエンド側の実装は特に必要ありません。
生成コマンド実行時に指定した名前でコンポーネントのファイルを作成し、router.tsxでのルーティングの定義にそのコンポーネントを追加します。
この時、Storybookとテストのファイルも自動的に生成されます。
Pageの生成コマンドの使いどころ
PageのStorybookとテストを生成する必要がある場合は、このコマンドを使う方が生産性が高いかと思います。
ただし、Redwood.jsの構成上、Pageの責務は極めて小さく、GraphQL関連のロジックはCellで管理されるため、Pageで管理するものは小さくなります。
その際、StorybookとテストはCellに寄せるという方が、重複は少ないかと思います。
とりあえずページコンポーネントを作成する時には便利かと思いますが、ページ全体のStorybookが必要でない場合はかえって不要なファイルが増えてしまうので、Storybookでページを作らなくていい方針であれば、生成は不要です。
Cell
# Cellの生成コマンド
yarn rw g cell ModelName
CellはRedwood.js固有の概念です。
GraphQLのクエリに対応するコンポーネントで、リクエストの状態管理を隠蔽してくれます。
Success, Empty, Loading, Errorという名前のコンポーネントを定義すると、GraphQLのリクエストの状態に応じてそれぞれのコンポーネントが描画されます。
生成されるコンポーネントはファイル名によって一覧、詳細、編集、作成用のテンプレートで生成されます。
また、Successコンポーネントのpropsの型はGraphQLのスキーマから自動生成されます。
ファイルの末尾がCell.tsxという名前であれば、Cellとして扱われます。
Cellの生成コマンドの使いどころ
CellコンポーネントはGraphQLクエリが新たに必要になった時には、毎回生成するのが良さそうです。
ただし、すでに類似したCellコンポーネントがある場合は生成コマンドではなく、複製で対応することもあるかと思います。
Component
# Componentの生成コマンド
yarn rw g component ModelName
PageとCell以外のコンポーネントをComponentと呼んでいます。
Componentの生成コマンドは指定された名前で、reactコンポーネントをStorybookと合わせて生成します。
Componentの生成コマンドの使いどころ
生成されるコードの量と複雑さから考えて、このコマンドを使うことはほとんどないかと思います。
同様の内容をコードスニペットから実装する方が良いでしょう。
生成コマンドを利用した実装プロセス
生成コマンドの中でも、Scaffoldは強力ですが、Storybookとテストが生成されないという問題を抱えています。
そのため私自身は以下のようなプロセスで開発の中で生成コマンドを利用しています。
- 必要なモデルのPrismaスキーマを全て定義、マイグレーションを実行
- Scaffoldで一旦動くものを立ち上げる
- Service, CellなどStorybook, テストが不足している部分について個別に生成し置き換え
- 変更をコミット
- ロジック、デザインの詳細の実装
この順番で実行することで、常に動くものがある状態で開発を進めることができるので、
- デモがしやすくなる
- 進捗が見えやすくなる
- 実装のイメージが付きやすくなる
というメリットがあります。
まとめ
Redwood.jsはコード生成が非常に強力なフレームワークです。
Ruby on Railsに引けを取らない初速と、React, TypeScript, Prismaの恩恵を受けた開発体験を提供してくれます。
すぐに動くものが欲しいということと、Storybookやテスト環境を整えたいという希望を同時に叶えてくれます。
スタートアップなどのプロジェクトの初速を特に重視するチームでは十分選択肢に入るのではないかと思います。