RedwoodJSというフレームワークがめちゃくちゃ良さそうなので入門してみます。
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
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だけでなく、ルートなども型を生成してくれるとか。すごい
良いですねー。create時点でlaunch.jsonもあるのは良いですねー。
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/public
よくある公開ディレクトリ
web/src/components
コンポーネントとCellコンポーネントが格納されている
web/src/layouts
ページ間で共有されるレイアウト部品ラッパー
web/src/pages
ページを構成する要素を集めたページコンポーネント
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
PostCell.mock.ts
PostCell.stories.tsx
PostCell.test.tsx
生成されたファイルはこんな感じ
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並なので、かなり強力なフレームワークだと感じました。 なんか軽いアプリケーション書いてみたい。