T3 StackはなぜJSONでDate型を扱えるのか
はじめに
T3 Stack
とは、Theo氏によるNext.jsをベースとしたWebアプリのテンプレートです。
動作確認したバージョン
"ct3aMetadata": {
"initVersion": "7.3.2"
}
疑問
T3 StackでAPIのパラメータにDate型のデータを設定したい時、例えばこのように書けるわけですが、
- サーバ側
getAll: publicProcedure
.input(
z.object({
date: z.date(),
})
)
.query(({ ctx, input }) => {
return ctx.prisma.example.findMany({
where: {
createdAt: {
gt: input.date,
},
},
});
}),
- クライアント側
const date = new Date(2023, 5, 6);
const { data, isSuccess } = api.example.getAll.useQuery({ date });
なぜ、このように書けるのでしょうか。
リクエストのContent-Typeはapplication/json
なのですが、HTTPでJSONを送るのにクライアント側でJSON.stringify
したり、サーバ側でJSON.parse
したりしなくていいのでしょうか。また、どうやってその変数がDate型であると伝えるのでしょうか。
答え
答えはtRPC
の公式ドキュメントに載っていました。T3 StackではtRPCを使ってサーバ-クライアント間のAPIを実現しているのですが、Data Transformerという機能でDate型を変換してくれているようです。そして変換処理を担っているのはsuperjson
というものだそうです。
SuperJSON allows us to transparently use, e.g., standard Date/Map/Sets over the wire between the server and client. That is, you can return any of these types from your API-resolver and use them in the client without having to recreate the objects from JSON.
transparently
とは、おそらく「(Date/Map/Setsなどを送信前にシリアライズしてから、受け取り時にデシリアライズしていることを)意識する必要なく」というような意味でしょう。日本語だとそのまま透過的
というようです。
- サーバ側
const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape }) {
return shape;
},
});
- クライアント側
export const api = createTRPCNext<AppRouter>({
config() {
return {
/**
* Transformer used for data de-serialization from the server
* @see https://trpc.io/docs/data-transformers
**/
transformer: superjson,
/**
* Links used to determine request flow from client to server
* @see https://trpc.io/docs/links
* */
links: [
loggerLink({
enabled: (opts) =>
process.env.NODE_ENV === "development" ||
(opts.direction === "down" && opts.result instanceof Error),
}),
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
}),
],
};
},
/**
* Whether tRPC should await queries when server rendering pages
* @see https://trpc.io/docs/nextjs#ssr-boolean-default-false
*/
ssr: false,
});
superjsonとは
TypeScriptの標準JSONモジュールの上位互換のようなライブラリのようです。READMEに比較表が載っていますが、Date/Map/Set以外にもundefinedや正規表現、エラーなども扱えるようです。
実際のリクエストはどうなっているのか
ChromeのDevToolでリクエストのペイロードをのぞいてみると、以下のようにJSON内にDate型であることを表すメタデータを追記しているようです。
ちなみに
tRPCのtransformer
オプションをコメントアウトしてみると、
const t = initTRPC.context<typeof createTRPCContext>().create({
// transformer: superjson,
errorFormatter({ shape }) {
return shape;
},
});
フォーマットエラーになりました。確かにsuperjsonが仕事しています。
まとめ
便利なフレームワークを使っていると色んなことが当たり前のようにできてしまいますが、なぜできているのか?と疑問を向けてみると、その当たり前を支える技術が存在していることに気付かされます。tRPCに感謝。superjsonに感謝。
宣伝
先日、本を書きました!200円で販売中です(無料部分あり)。
T3 Stack(Next.js, tRPC, Prisma, NextAuth.js, TailindCSS)を使ったSNSのWebアプリケーション開発について、ユーザーとユースケースの定義から作ったWebアプリのデプロイまで、一通り説明しています。よかったら読んでみてください!
Discussion