1️⃣
Next.js/appディレクトリ用のRPCライブラリを作った
TL;DR
ルーティングは全てフレームワークに任せてエンドポイント一つだけ定義するためのRPCライブラリを作りました。Next.js/appディレクトリで運用しているとき内部APIを作るのに便利です。
背景
Wiktionaryのデータを使って英英辞書のウェブアプリをNext.js/appディレクトリでちょこちょこ作っています。内部APIにはtRPCを使っていましたが、いくつか不満がありました。
- 基本的に複数のプロシージャをRouterで一つのエンドポイントにまとめる仕様になっているが、Vercelのエッジランタイムで動くような軽いものは別のエンドポイントでデプロイしたい
- Next.js/appディレクトリのエンドポイントを一つずつ定義するスタイルと相性が悪い
- ストリーミングレスポンスに対応しておらず、そのようなエンドポイントには生HTTPを使う必要がある
そんな中で以下のような記事を見て、RPCライブラリを自作することにしました。
コンセプト
名前はoneRPC。Readmeに書いてある通りですが、
- ローカルの関数を呼ぶようにサーバサイドの関数をクライアントから呼べる
- TypeScript向けの型安全なAPI
- ルーティングは提供しない。フレームワークやインフラに任せる
- HTTPの機能をなるべく活用する(ヘッダ等)
- Web APIにしか依存しない。エッジランタイムで動く
- ストリーミングレスポンスのサポート
上記の記事にあるようなRequest・Response型を直接使うことは諦めました。そのかわりに(input: T) => S
型のサーバサイド関数を(request: Request) => Promise<Response>
型に変換するだけのミニマルな設計のAPIにしました。
API
今の所query
, queryStream
, mutate
関数があります。それぞれonerpc
とonerpc/client
モジュールから利用可能で、それぞれサーバサイドの手続き定義、クライアントサイドの手続き呼び出しに使います。query*
関数はGET
メソッドにmutate
関数はPOST
メソッドに対応します。Zodか独自の関数で入力と出力の検査ができます。
tRPCにあるような自動バッチは今の所サポートしていません。(サポートしたとしても一つの手続きしかまとめられないので。)
Next.js/appディレクトリでの利用例
app/api/foo/route.ts
:
import { query } from "onerpc";
import { z } from "zod";
export const GET = query(z.number(), z.string(), (x) => `Hello, ${x}!`, {
path: "/api/foo",
});
app/page.tsx
:
import { type GET } from "@/app/api/foo/route";
import { query } from "onerpc/client";
export default async (): Promise<JSX.Element> => (
<div>{await query<typeof GET>("/api/foo", 42))}</div>
);
感想
- 正直、Next.jsがServer Actionsみたいないい感じAPI出してくれないかな... なんで現状mutationのみのサポートなんだろう。全てサーバコンポーネントにしてということ?
- Hono.jsがストリーミングとappディレクトリに対応したらそっちにするかも
結論
まあまあ便利。
Discussion