RedwoodJSというフレームワークがめちゃくちゃ良さそうなので入門してみます。
RedwoodJS is 何?
Redwood is the full-stack web framework designed to help you grow from side project to startup. Redwood features an end-to-end development workflow that weaves together the best parts of React, GraphQL, Prisma, TypeScript, Jest, and Storybook.
サイドプロジェクトからスタートアップくらいのプロダクトの成長を支援するためのフルスタックフレームワークとのことで、Railsと近い立ち位置にいるような印象。
React、GraphQL、Prisma、TypeScript、Jest、Storybookを用いたフレームワーク。 選定しているライブラリがとても自分好みです。
GitHub共同創設者、jekyllの作者、Gravatarの作者、Semantic Versioningの著者、TOMLの発明者など錚々たるメンバーに寄って立ち上げられたOSSプロジェクト。
まずはインストール
> yarn create redwood-app try-redwoodjs --typescript
フォルダ構成
.redwood .vscode api ├ db ├ src │ ├ directives │ ├ functions │ ├ graphql │ ├ lib │ └ services └ types scripts web └ public └ src ├ components ├ layouts └ pages
.redwoodディレクトリ
The CLI automatically generates types for you. These generated types not only include your GraphQL queries, but also your named routes, Cells, scenarios, and tests.
いきなりめちゃくちゃ良さそうなこと書いてある GraphQLだけでなく、ルートなども型を生成してくれるとか。すごい
.vscodeディレクトリ
良いですねー。create時点でlaunch.jsonもあるのは良いですねー。
apiディレクトリ
api/db
Prismaのスキーマが入ってるここをいじってdbを作っていく
api/src
サーバーサイドコードを入れる箱中で更に分類する
api/src/directives
GraphQLのディレクティブを定義するところ
api/src/functions
自動生成されるgraphql.jsファイルと、lambdaファンクションが入るらしい、リンクがNetlifyのfunctionsになっているがそこにデプロイされるの?どういうこと?
api/src/graphql
GraphQLスキーマを定義するところっぽい。コードファーストはできない?
api/src/lib
認証や、db接続に使える関数が置いてある。そういうレイヤーの関数をおくところっぽい
api/src/services
ビジネスロジックを格納するところ。scriptからも呼べるような依存性にしておくと良いっぽい
webディレクトリ
web/public
よくある公開ディレクトリ
web/src/components
web/src/layouts
ページ間で共有されるレイアウト部品ラッパー
web/src/pages
ページを構成する要素を集めたページコンポーネント
Cell?
web/src/components/Post/PostCell/PostCell.tsx
規約通りの命名でexportすると、Redwoodがライフサイクル管理や、型の生成をしてくれるらしい。 特に特徴的なのがGraphQLクエリのvariables
query FindPostById($id: Int!) { ...
というクエリを書くと
<PostCell id={3} />
のようにidのpropが生えるみたい。なるほど。なかなかよいかもしれない。
また、CLIで生成できる。
yarn rw generate cell post
このコマンドでいかが生成された
生成されたファイルはこんな感じ
PostCell.tsx
import type { FindPostQuery } from 'types/graphql' import type { CellSuccessProps, CellFailureProps } from '@redwoodjs/web' export const QUERY = gql` query FindPostQuery($id: Int!) { post: post(id: $id) { id } } ` export const Loading = () => <div>Loading...</div> export const Empty = () => <div>Empty</div> export const Failure = ({ error }: CellFailureProps) => ( <div style={{ color: 'red' }}>Error: {error.message}</div> ) export const Success = ({ post }: CellSuccessProps<FindPostQuery>) => { return <div>{JSON.stringify(post)}</div> }
PostCell.mock.ts
// Define your own mock data here: export const standard = (/* vars, { ctx, req } */) => ({ post: { id: 42, }, })
PostCell.stories.tsx
各状態のストーリーをそのまま書けるのでこれはめっちゃはかどるのでは...
import { Loading, Empty, Failure, Success } from './PostCell' import { standard } from './PostCell.mock' export const loading = () => { return Loading ? <Loading /> : null } export const empty = () => { return Empty ? <Empty /> : null } export const failure = () => { return Failure ? <Failure error={new Error('Oh no')} /> : null } export const success = () => { return Success ? <Success {...standard()} /> : null } export default { title: 'Cells/PostCell' }
PostCell.test.tsx
storybookと同じく各状態のコンポーネントのレンダリングを即テストできるので捗りそう
import { render, screen } from '@redwoodjs/testing/web' import { Loading, Empty, Failure, Success } from './PostCell' import { standard } from './PostCell.mock' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float and DateTime types. // Please refer to the RedwoodJS Testing Docs: // https://redwoodjs.com/docs/testing#testing-cells // https://redwoodjs.com/docs/testing#jest-expect-type-considerations describe('PostCell', () => { it('renders Loading successfully', () => { expect(() => { render(<Loading />) }).not.toThrow() }) it('renders Empty successfully', async () => { expect(() => { render(<Empty />) }).not.toThrow() }) it('renders Failure successfully', async () => { expect(() => { render(<Failure error={new Error('Oh no')} />) }).not.toThrow() }) // When you're ready to test the actual output of your component render // you could test that, for example, certain text is present: // // 1. import { screen } from '@redwoodjs/testing/web' // 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument() it('renders Success successfully', async () => { expect(() => { render(<Success post={standard().post} />) }).not.toThrow() }) })
どんなコマンドがあるのか
良さそう
> rw <command> Commands: rw build [side..] Build for production rw check Get structural diagnostics for a Redwood project (experimental) [aliases: diagnostics] rw console Launch an interactive Redwood shell (experimental) [aliases: c] rw data-migrate <command> Migrate the data in your database [aliases: dm, dataMigrate] rw deploy <target> Deploy your Redwood project rw destroy <type> Rollback changes made by the generate command [aliases: d] rw dev [side..] Start development servers for api, and web rw exec [name] Run scripts generated with yarn generate script rw generate <type> Generate boilerplate code and type definitions [aliases: g] rw info Print your system environment information rw lint [path..] Lint your files rw prerender Prerender pages of your Redwood app at build time [aliases: render] rw prisma [commands..] Run Prisma CLI with experimental features rw record <command> Setup RedwoodRecord for your project. Caches a JSON version of your data model and adds api/src/models/index.js with some config. rw serve [side] Run server for api or web in production rw setup <commmand> Initialize project config and install packages rw storybook Launch Storybook: a tool for building UI components and pages in isolation [aliases: sb] rw test [filter..] Run Jest tests. Defaults to watch mode rw ts-to-js Convert a TypeScript project to JavaScript rw type-check [sides..] Run a TypeScript compiler check on your project [aliases: tsc, tc] rw upgrade Upgrade all @redwoodjs packages via interactive CLI Options: --help Show help [boolean] --version Show version number [boolean] --cwd Working directory to use (where `redwood.toml` is located.) Examples: yarn rw g page home / "Create a page component named 'Home' at path '/'"
感想
思ったよりとても重厚なフレームワークでした。 思想はとても良く、元々Apolloのベストプラクティスに共感できる場合は結構マッチするのではないかと思います。
良さそうなポイント - 基本的にはRedwoodJSが全てのツールをラップしているので、パッケージ依存バージョンの動作保証をRedwoodJSにある程度任せられそう - test, storybookを即書ける - 単純なWebアプリケーションはRailsのActionView並に高速に実装できそう - それでいてReact, GraphQLを使っているのでUIを柔軟にできる
懸念 - RedwoodJSへのロックイン
特にtest, storybookを即かけるのはめちゃくちゃ良さそう。その上単純なページを作る速度もRails並なので、かなり強力なフレームワークだと感じました。 なんか軽いアプリケーション書いてみたい。