🔥
tRPCでPrismaのDate型がstringで帰ってくる問題とsuperjsonによる解決策
背景
再現に必要な各種packageのバージョンは以下の通りです。
- Next.js 15.0系
- Prisma 5.系統
- trpc 10.系統
trpcで取得したdataをfrontendで受け取ってuseStateのstateとして取り込もうとした際に下記のエラーが生じました。
import { useState, useEffect } from 'react';
import { User } from '@prisma/client';
import { trpc } from '@/server/trpc/trpcClient';
// (中略)
const Page = () => {
const [users, setUsers] = useState<User[]>();
const { data } = trpc.getUsers.useQuery();
useEffect(() => {
if (data) {
setUsers(data);
}
}, [setUsers, data]);
return <div>{users && users[0].name}</div>;
};
export default Page;
Types of property 'created_at' are incompatible.
Type 'string | null' is not assignable to type 'Date | null'.
Type 'string' is not assignable to type 'Date'.ts(2345)
解決策
基本的にこのコミュニティで議論されている通りでした。
結論としてはServer側とClient側との両方でtransformerにsuperjsonを設定することで解決しました。trpcの公式ドキュメントでも、
transformerにsuperjsonを設定するプロセスが記載されています。
以下は公式ドキュメントの転載です。
Server側
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
export const t = initTRPC.create({
transformer: superjson,
});
Client側
(Next.jsの場合)
import { createTRPCNext } from '@trpc/next';
import type { AppRouter } from '~/server/routers/_app';
import superjson from 'superjson';
// [...]
export const trpc = createTRPCNext<AppRouter>({
config({ ctx }) {
return {
transformer: superjson, // <-- ここに追加
};
},
// [...]
});
私の場合、trpcのproviderのpageを参考にして、以下のように設定しました。
export function App() {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
transformer: superjson, // <-- ここに追加
links: [
httpBatchLink({
url: 'http://localhost:3000/trpc',
async headers() {
return {
authorization: getAuthCookie(),
};
},
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{/* Your app here */}
</QueryClientProvider>
</trpc.Provider>
);
}
何が問題だったの?
tRPCは基本的に型安全なAPI通信を実現するために、デフォルトでは標準のJSONシリアライズを使用します。
しかし、困ったことにJSONシリアライズでは日付やマップ、セットなどの特殊な型を扱うのが難しく、Date型などで正確なデータの受け渡しができない場合があるようです。
そこで、superjsonを導入することで、Date型も正しくシリアライズ/デシリアライズできるようになるということでした。
試しておりませんが、transformerをカスタマイズしても同様の効果が得られるようです。
Discussion