はじめに
APIレスポンスやサーバーサイドのpropsにDateオブジェクトを渡したら、いつの間にか文字列になっていた——そんな経験はありませんか。JSON.stringifyは標準のJSONに存在しない型を正しく扱えず、DateはISO文字列に、MapやSetは{}や[]に変換され、BigIntに至ってはエラーで落ちてしまいます。
結果として、受け取った側でnew Date(str)のような変換処理をあちこちに書くことになり、型定義とのズレやうっかりミスの温床になりがちです。この記事では、そんなJSONの限界をそのまま乗り越えられるライブラリ「Superjson」を紹介します。
Superjsonとは
Superjsonは、JavaScriptの豊富な型をJSONの形を保ったままシリアライズ・デシリアライズできるライブラリです。Blitz.jsチームが開発しており、tRPCの公式トランスフォーマーとしても採用されているため、フルスタックTypeScript構成では特に馴染み深い存在です。
主な特徴
- 豊富な型サポート - Date、Map、Set、RegExp、BigInt、Error、URLなど標準JSONでは表現できない型をそのまま扱えます
- 型安全性 - TypeScriptと組み合わせることで、シリアライズ前後の型がずれにくくなります
- 軽量・依存最小 - ランタイムのオーバーヘッドが小さく、依存パッケージも最小限です
- フレームワーク非依存 - Next.jsやtRPCに限らず、任意のJavaScript/TypeScriptプロジェクトで利用できます
インストール
npm install superjson
yarn add superjson
pnpm add superjson
基本的な使い方
JSON.stringify/JSON.parseをそのまま置き換える感覚で使えます。
import superjson from "superjson";
const data = {
createdAt: new Date("2026-01-01T00:00:00.000Z"),
tags: new Set(["typescript", "javascript"]),
meta: new Map([["views", 1200]]),
bigNumber: 9007199254740993n,
};
// シリアライズ
const { json, meta } = superjson.serialize(data);
console.log(json);
// => Dateは文字列に、Setは配列になるが、metaに型情報が保持される
// デシリアライズ
const restored = superjson.deserialize({ json, meta });
console.log(restored.createdAt instanceof Date); // true
console.log(restored.tags instanceof Set); // true
console.log(typeof restored.bigNumber); // "bigint"
文字列としてやり取りしたい場合はstringify/parseを使います。
const str = superjson.stringify(data);
const parsed = superjson.parse<typeof data>(str);
console.log(parsed.createdAt instanceof Date); // true
serializeはjson(通常のJSON互換オブジェクト)とmeta(型情報)を分けて返すため、既存のJSON APIに型情報だけを追加で載せたい場合にも使いやすい設計です。
実践的なユースケース
tRPCのトランスフォーマーとして使う
tRPCではサーバー・クライアント間でDateやMapをそのままやり取りしたい場面が多く、Superjsonが公式に推奨されています。
import { initTRPC } from "@trpc/server";
import superjson from "superjson";
const t = initTRPC.create({
transformer: superjson,
});
export const appRouter = t.router({
getPost: t.procedure.query(() => {
return {
id: "1",
title: "Superjsonのすすめ",
publishedAt: new Date(), // クライアントでもDateオブジェクトとして受け取れる
};
}),
});
APIレスポンスの型復元を共通化する
自前のREST APIでも、レスポンスをSuperjsonでラップしておけば、フロントエンド側での型変換処理を1箇所にまとめられます。
// サーバー側
import superjson from "superjson";
export async function handler() {
const body = superjson.stringify({
id: crypto.randomUUID(),
expiresAt: new Date(Date.now() + 60 * 60 * 1000),
});
return new Response(body, {
headers: { "Content-Type": "application/json" },
});
}
// クライアント側
import superjson from "superjson";
async function fetchSession() {
const res = await fetch("/api/session", { method: "GET" });
const text = await res.text();
return superjson.parse<{ id: string; expiresAt: Date }>(text);
}
このように書いておけば、expiresAtは文字列ではなく最初からDateオブジェクトとして扱えるため、new Date()への変換忘れによるバグを防げます。
まとめ
Superjsonを使うことで、DateやMap、Set、BigIntといったJSONが本来扱えない型を、意識せずにシリアライズ・デシリアライズできるようになります。特にtRPCやNext.jsのようなフルスタックTypeScript構成では、サーバーとクライアントの型の一貫性を保つ強力な武器になります。
「JSONに渡すとDateが文字列になって困る」「型変換処理があちこちに散らばっている」と感じたら、ぜひSuperjsonの導入を検討してみてください。
