Redwood.jsで爆速TypeScript-Web開発

こんにちは、あるいはこんばんは。
[MedBridge](https://medbridge-micin.com/)の開発をしているエンジニアの眞嶋です。

https://medbridge-micin.com/

この記事は MICIN Advent Calendar 2022 の4日目の記事です。

前回は[tsugitta](https://zenn.dev/tsugitta)さんの

https://zenn.dev/tsugitta/articles/multiple-apis-with-apollo-client

でした。

今回はMedBridgeの中でも独立したサービスで採用したRedwood.jsについてご紹介します。
## この記事で書くこと
- Redwood.jsとは何か
- メリット
- デメリット
- 自分なりの使用方法
- ユースケース
- 使ってみた感想

## この記事で書かないこと
- React, PrismaなどのRedwood.js以外の技術の詳細

## Redwood.jsとは
React-GraphQL-Prismaで構成されたフルスタックフレームワークです。
下記のコマンドを打つだけで、フロント、バックエンド、テスト、Storybook環境を構築してくれます。


```bash
yarn create redwood-app プロジェクト名 --typescript
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/f770d845-756e-5145-20f6-bbc68c7a0dd5.png)
公式サイトより

https://redwoodjs.com/

## Redwood.jsプロジェクトの構成
Redwood.jsのプロジェクトはyarn workspaceを使ったモノレポプロジェクトです。
apiにバックエンド、webにフロントエンドのモジュールが格納されています。

![スクリーンショット 2022-11-29 19.23.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/efb2b2f3-dc32-abeb-5632-fea5e231cd5c.png)


## Redwood.jsの特徴
### Prisma、GraphQLスキーマからの自動生成
Redwood.jsはPrismaとGraphQLのスキーマからコードを生成する仕組みが非常に強力です。
どのような生成ができるか下記で説明します。

### コード生成の種類
#### Scaffold
```bash
# scaffoldの生成コマンド
yarn rw g scaffold ModelName
```
Ruby on RailsのScaffoldと同様に、フロントエンドとバックエンドのコードを一度に生成します。
scaffoldで画面を生成するときには基本的なCRUD操作は全てカバーされており、一覧、詳細、作成、編集のページが一度に生成されます。また、共通のレイアウトに削除ボタンも実装されています。


例えば、以下のようなPrismaのスキーマがあった場合、

```schema.prisma
model Post {
  id    Int    @id @default(autoincrement())
  title String
  body  String
}
```
以下のようなファイルが生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/f50a83ac-042a-5c0c-7272-bbc07ddf585c.png)

これにより、以下のPathにそれぞれページが生成されています。
- newPost(作成) -> /posts/new
- editPost(編集) -> /posts/{id:Int}/edit
- post（詳細） -> /posts/{id:Int}
- posts（一覧） -> /posts

**一覧ページ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/5703e454-e51f-de33-e464-c0738605b076.png)

**詳細ページ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/6bfbb0e1-c712-6f0a-aa4c-fed61bdec892.png)

**作成ページ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/fdaafa9d-a322-43e5-cf0c-2394551b7135.png)

**編集ページ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/7e2856cd-170f-6630-45a5-bb99cb782580.png)

管理画面ライクなUIでデータの作成、編集、閲覧が行えるので、PoC目的であればデータモデリングとScaffoldのみである程度は目標が達成できます。
早く動く状態を作るという意味では、他のフレームワークでは出せないスピードだと思います。
ただし、注意点として、他のRedwood.jsの生成コマンドとは異なり、フロントエンドのStorybookやテストは生成されません。

Redwood.jsの生成コマンドについて、より詳しい内容は下の記事にまとめたので、気になる方はこちらをお読みください。

https://qiita.com/mjm2kt/items/bf375778de6749650305

### Cellコンポーネント
CellはRedwood.js固有の概念です。
GraphQLファイル内で定義されているGraphQLのリクエストの状態の管理を隠蔽して
Success, Empty, Loading, Errorの各状態の時に表示するコンポーネントを定義すると
Redwood.js側でそれらを出し分けてくれます。
Loadingはreact suspenseのfallback, Errorはerror boundaryでのコンポーネントの出しわけにそれぞれ対応しています。

APIの状態管理と実装を切り離せるため、各コンポーネントの責務が小さくなり、全て一つのコンポーネントにまとめた時に比べて、メンテナンス性が高まります。
APIの状態管理が含まれていないため、Storybookとの相性も良いです。
Success, Empty, Loading, ErrorのそれぞれのコンポーネントでStorybookの表示を特に設定せずに、生成コマンドから生成されたコードをそのまま使うことができます。

### テスト
Redwood.jsはプロジェクト立ち上げた最初の段階からjestが設定されており、テスト実行後に、DBの変更をロールバックする仕組みが実装されているので、設定をせずにDBのテストを実行することができます。
テストについては公式のドキュメントが網羅的にまとまっているので、私が良いと思っているところの紹介にとどめます。

https://redwoodjs.com/docs/testing

#### バックエンドのテスト
Redwood.jsのバックエンドのテストは実際にDBへの接続を行うものが主体です。
scenarioという単位で各テストケースのデータを定義する仕組みが準備されています。
基本的なCRUDのテストは生成されるため、ビジネスロジックの実装に集中することができます。

#### フロントエンドのテスト
Scaffold以外のフロントエンドの生成コマンドはStorybookを使ったテストを自動生成してくれます。
Storybookで表示するデータをテスト環境とStorybook環境で利用できるようになっています。
デフォルトではStorybookをレンダリングして、エラーが起きないかをチェックするコードのみ生成されます。
Cellのテストではテストデータを定義する仕組みも用意されています。

## Redwood.jsのユースケース
Redwood.jsは特に以下の状況でその強みを発揮します。

### スタートアップでのWeb開発
スタートアップでは特に、短いサイクルでの仮説検証が必要なため、プロジェクト初期でのスピードが求められます。
しかし、StorybookやDBのテスト、リントなどプロジェクトの最初から整えていないと後から整えることが難しいものも、蔑ろにしたくはありません。
Redwood.jsは、これらのプロジェクト初期の問題を一挙に解決してくれます。
強力なコード生成により、動くコードとStorybook、テストコードをプロジェクトの序盤からほぼ作業せずに用意できることはRedwood.jsの最大の強みかと思います。

### TypeScript x GraphQLでバックエンドとフロントエンドを統一したい
近年ではバックエンドとフロントエンドの言語を統一することで、開発者の認知負荷を下げる目的で、バックエンドをTypeScriptで記述するチームが増えてきています。
そうした場合、今から始めるプロジェクトではReact, GraphQL, Prismaの構成が第一候補となるかと思います。
Redwood.jsは、これらのスタックでほぼ設定なしでプロジェクトが始められるため、バックエンドとフロントエンドのコードを別々で立ち上げるよりも、プロジェクト初期の時間をより生産的に使うことができます。

## 課題
Redwood.jsはまだまだ新興のフレームワークのため課題も多くあるかと思います。
私が特に課題に感じているのは下記の点です。

### Serviceの責務が大きい
Redwood.jsの用意されたテンプレート通りのファイル構成にすると、Serviceの責務が大きくなります。
アプリケーションコードと呼ばれる手続的な処理と、ビジネスロジック、データの取得が全てServiceに含まれるため、大規模プロジェクトではServiceの肥大化が問題になることが予想されます。

### テストの立ち上がりが遅い
また、テストの実行前に毎回Prisma Clientの生成とDBのリセットを行う処理が入ります。
この処理は、Prisma Clientを利用するか否かに関わらず実行されます。
TDDなどのテストを頻繁に確認するスタイルのプログラミングを行う場合は、すぐに結果が確認できないことによって大きく生産性が下がることもあります。

こちらについては下記の記事で解消法を紹介しています。
https://qiita.com/mjm2kt/items/7d7941ad247fa83addbb

### AWSでのデプロイがややこしい
公式ドキュメントではServerless Functionを使ったデプロイが紹介されています。
これはAWS CloudFormationを使ったインフラ環境の構築になりますが、S
3の設定を変更できないなどの、制約があります。
Terraformを使った環境構築をしている場合は、現在時点では、AWS Lambdaへのデプロイはできないようです。
私たちのチームでは、代わりにDockerコンテナからECSにデプロイする方法を採用しています。

### 型がややこしい
現在時点でRedwood.jsの型定義にはいくつか問題があると感じています。

#### defaultでstrictになっていない
Redwood.jsを立ち上げる際に--typescriptオプションをつけてプロジェクトを立ち上げた際にはプロジェクトのどのファイルでもtsconfig.jsonをstrictオプションはfalseになっています。

現時点でのcanaryリリースのドキュメントにはstrict:trueに設定することができる旨のページが存在しているので近いうちにstrict:trueの設定ができるようになりそうです。（Redwood.jsの開発スピードは非常に早いため、年内にもリリースされそうです。）

https://redwoodjs.com/docs/canary/typescript/strict-mode

#### GraphQLのResolverに外部APIからのデータ取得をする際の型の問題
Redwood.jsのGraphQLの型生成はSDLとPrismaのスキーマから生成されるため、SDLのみ存在し、Prismaのスキーマがない場合は、意図しない型が生成されることがあります。

### アップデートが早い
これはメリットでもデメリットでもありますが、Redwood.jsのバージョンアップのスピードは凄まじいものがあります。
2022年の4月に1.0.0がリリースされてから、現在（2022年11月28日）時点で3.5.0がリリースされています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/ade36335-1534-db40-93e7-66d9b34e8951.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181998/11349b96-ec11-429f-b89f-cc3b0947d90f.png)
今までのメジャーアップデートでも、主なAPIの破壊的変更は行われていませんが、アップデートに伴って利用者としても追従していく必要があります。(過去のメジャーバージョンの更新はデフォルトのLintのルールの変更とnode.jsのバージョンアップでした。)

## おわりに
今回Redwood.jsで実際に開発してみて、検証用のプロジェクトだったこともあり、
かなり私のチームの求めているものに合致した感触がありました。
今回のプロジェクトでは、プロジェクト初期の段階でDBのスキーマを決め、全てのモデルでコード生成をしてから開発をするというスタイルで行いました。
そうすることによって、管理画面ライクに全機能が揃った状態になっていたので、他のメンバーの作業がボトルネックになりづらくなっていました。
当初は初速の速さを重視して選んだフレームワークでしたが、開発メンバー同士でほとんどコンフリクトさせずに開発することができたので、期待以上の開発スピードを出すことができました。
外部APIの利用などRedwood.jsチームが想定していない利用方法をしようとすると難しい場面もありましたが、
その部分を含めても、0から別のフレームワークで開発するよりも効率が良かったと感じています。
次回のメジャーアップデートで公式でのTypeScriptのstrictモード対応が含まれているので、それが反映されるとさらに安全で高速に開発ができると思います。
Next.js, Remix, などTypeScriptのフレームワークは戦国時代を迎えつつありますが、React x GraphQL x Prismaでサービスをスピード重視で開発したいという場合、Redwood.jsは選択肢の上位に挙げられるフレームワークかと思います。

---
MICINではメンバーを大募集しています。
「とりあえず話を聞いてみたい」でも大歓迎ですので、お気軽にご応募ください！
MICIN採用ページ：https://recruit.micin.jp/