aspida/frourioは何をしているのか。極小プロジェクトに導入して理解するハンズオン
極小というのは local minimum という意味です。
サーバーはたった1ファイルの index.ts
のみ、フロントエンドは create-vite
のテンプレートの状態から解説します。とても簡単なので是非見ていってください。
Aspida と Frourio
フロントエンドとHTTP APIサーバーを用意 (スキップ可能)
ここは本質ではないため、 git clone https://github.com/frouriojs/learn-aspida-frourio-handson
としてしまっても問題ない。ただし、こちらは pnpm
を用いているため、事前に npm i -g pnpm
などでインストールしておく必要がある。
clone してすすめるかたは #まずはAspidaを導入 まで飛ばしていただいて構わない。
さて、以下はテンプレートと同様に pnpm
を使う。これは主に pnpm
が提供するモノレポサポートの恩恵を得るためだが、 npm
でも問題ない。(その場合は適宜読み替えてほしい)
フロントエンドは create-vite
を使って簡単なものを用意する。
mkdir learn-frourio
cd learn-frourio
pnpm init -y
your_editor pnpm-workspace.yaml
pnpm-workspace.yaml
は以下のように編集。
packages:
- 'front'
- 'server'
これで learn-frourio/front/
と learn-frourio/server/
がモノレポの対象になる。
次に vite
によるフロントエンドをテンプレートから生成。
pnpm create vite -- --template react-ts front
pnpm i
続いて、サーバーを用意。たったの1ファイルなので安心してほしい。
mkdir server
cd server
pnpm init -y
pnpm i fastify fastify-cors
pnpm i typescript ts-node @types/node -w # こちらはルートに追加している
your_editor index.ts
index.ts
を以下のように編集する。
import FastifyCors from 'fastify-cors'
import Fastify from 'fastify'
const fastify = Fastify();
fastify.register(FastifyCors, {})
fastify.get('/', (req, reply) => {
reply.send({hello: 'world'})
})
fastify.listen(8888, '0.0.0.0')
pnpm exec ts-node index.ts
# 別のターミナルで
curl localhost:8888
JSONが正しくみれれば成功。
ルーティングが一個だとわかりにくいので、もう一つ実装しておきます。
fastify.get('/', (req, reply) => {
reply.send({hello: 'world'})
})
+ fastify.get('/hi', (req, reply) => {
+ reply.send({hello: 'how are you?'})
+ })
fastify.listen(8888)
また、ブラウザコンソールで fetch("http://localhost:8888")
をしておいて到達を確認しておいてください。
まずはAspidaを導入
さて、フロントエンドから先程作ったサーバーにアクセスしたいです。fetch("http://localhost:8888")
のように書いていってもいいですが、型がほしいです。そこでAspidaを導入します。
cd ../front
pnpm i aspida @aspida/fetch
開発サーバーを立ち上げておいても良いです。
pnpm run dev -- --host
デフォルトで http://localhost:3000
にアクセスすれば見れると思います。
まず型定義を書きます。 mkdir -p api/hi
として、 api/index.ts
と api/hi/index.ts
を以下の内容に編集します。
export type Methods = {
get: {
resBody: {
hello: string;
}
}
}
pnpm exec aspida
api/$api.ts
が生成されます。これには、先程の index.ts
の内容が集約されています。
touch ./src/api-client.ts
your_editor ./src/api-client.ts
front/src/api-client.ts
を以下のように編集します。
import $api from '../api/$api';
import aspida from '@aspida/fetch';
export const api = $api(aspida(undefined, {
baseURL: 'http://localhost:8888',
}));
aspida
に渡した undefined
はデフォルト引数( global
の fetch
オブジェクト)を使わせるためです。fetch
を渡しても良いです。
これだけで完了しました。 front/src/api-client.ts
の下の方で api.
などと書くとパスが補完されると思います。
front/src/App.tsx
を以下のように編集してください。
+ import {api} from './api-client'
function App() {
const [count, setCount] = useState(0)
+ const [greeting, setGreeting] = useState('loading...')
+ useEffect(() => {
+ api.$get().then(res => {
+ setGreeting(res.hello);
+ })
}, [])
これで greeting
ステートにfetchした値が入ります。
あとはすきなところに表示しましょう。
- <p>Hello Vite + React!</p>
+ <p>{greeting}</p>
サーバーにFrourioを導入
さて、今度はサーバーも先程定義した api/.../index.ts
の型を保証した状態で開発したいです。
そこで次は frourio
を導入します。 aspida
も必要になるので aspida
も入れておきます。 (フロントからは消しても良いです、 @aspida/fetch
はランタイムとして必要なので残してください。)
cd ../server
pnpm i -D frourio aspida
mkdir api
pnpm exec frourio --watch
# 別のターミナルで
mkdir api/hi
さて、frourio
を実行すると$server.ts
が、mkdir
を実行すると、api/
配下に index.ts
, controller.ts
, $relay.ts
が出現したと思います。
index.ts
は先程の aspida
で使ったような型定義を書く場所です。先程のfront/api/.../
内の型定義をこちらに書き写しておいてください。その後消して問題ないです。
controller.ts
にはサーバーの実装を書きます。次のように書き換えます。
import {defineController} from './$relay'
export default defineController(() => ({
- get: () => (...)
+ get: () => ({status: 200, body: {hello: 'world'}})
}))
先程書いた index.ts
の型に違反する形で書くとエラーになります。
api/hi/controller.ts
のほうも書き換えておいてください。
最後にaspida
を実行します。これで先程と同じように api/$api.ts
が生成されます。
frourio --watch
の方は終了しても問題ありません。
index.ts
で frourio
で書いた定義を使うように書き換えます。
import Fastify from 'fastify'
+ import $server from './$server';
const fastify = Fastify();
fastify.register(FastifyCors, {})
- fastify.get('/', (req, reply) => {
- reply.send({hello: 'world'})
- })
-
- fastify.get('/hi', (req, reply) => {
- reply.send({hello: 'how are you?'})
- })
+ $server(fastify);
fastify.listen(8888, '0.0.0.0')
最後に、front/src/api-client.ts
を書き換えます。APIの型定義をおいている位置が変わったためです。
-import $api from '../api/$api';
+import $api from '../../server/api/$api';
さて、 pnpm exec ts-node index.ts
を再起動します。
フロントエンドからみて問題なく動いていれば成功です。
PR: この内容で 【frourio LT会】TypeScriptでフルスタックWeb開発!初心者歓迎! に登壇する予定です。そこではさらに、各ファイルのより詳しい説明や frourio
の嬉しさについて話すつもりdす。
Discussion