P
Prisma2mo ago
Florian

Type-safety for dates when returning DB entries as JSON

I used to use Prisma in server components where I don't need to do serialization. Now I need to return DB entries from an API route handler in JSON form. This means, the Date type of the Prisma model is not correct on the client anymore. JSONification turns the Date into a string. What's the correct way to handle this on the client so I have type-safety?
const posts = await prisma.post.findMany({
include: getPostWithReplyToInclude(user?.id),
orderBy: {
createdAt: "desc",
},
take: pageSize + 1,
cursor: cursor ? { id: cursor } : undefined,
});

const nextCursor = posts.length > pageSize ? posts[pageSize].id : null;

const responseBody: PostsPage = {
posts: posts.slice(0, pageSize),
nextCursor,
};

return Response.json(responseBody);
const posts = await prisma.post.findMany({
include: getPostWithReplyToInclude(user?.id),
orderBy: {
createdAt: "desc",
},
take: pageSize + 1,
cursor: cursor ? { id: cursor } : undefined,
});

const nextCursor = posts.length > pageSize ? posts[pageSize].id : null;

const responseBody: PostsPage = {
posts: posts.slice(0, pageSize),
nextCursor,
};

return Response.json(responseBody);
7 Replies
moosthuizen
moosthuizen2mo ago
@Florian Assuming that Response.json uses JSON.stringify somewhere under the hood, the Date object will get converted into an ISO formatted string. On the client, you will need to manually do the reverse. Assuming you use JS, intercept the response and update the specific fields you know to be returning timestamps using the Date constructor. Also consider whether you really need a Date type on the client. date-fns for example (which I use to format dates nicely and get relative distance, eg. "a few minutes ago") accepts either Date objects or strings, which get automatically parsed.
Florian
Florian2mo ago
Thank you! I don't necessarily need the data, but I want a type that doesn't lie to me.
moosthuizen
moosthuizen2mo ago
np
jonfanz
jonfanz2mo ago
If I'm understanding correctly, you have access to the Prisma types on the client, but when you try and use them there is a discrepancy because she shape of Post has createdAt as a Date while on the frontend it's a string. Is that right?
Florian
Florian2mo ago
Right. I just want to know how this is commonly handled? The best approach seems to be to parse it back to Date when I retrieve the json response. I don't want to litter my components with Jsonify<T>
jonfanz
jonfanz2mo ago
Forgive me because I'm still a bit of a frontend newbie 😅 But to me it seems that on the frontend you could do make a type like the following: (horrible pseudocode incoming)
import { Prisma } from '@prisma/client'

export type PrismaUser = Omit<Prisma.UserGetPayload, 'createdAt'> & { 'createdAt': string }
import { Prisma } from '@prisma/client'

export type PrismaUser = Omit<Prisma.UserGetPayload, 'createdAt'> & { 'createdAt': string }
would that work?
Florian
Florian2mo ago
I think so. But I decided to create a wrapper around fetch that parses timestamp strings back to Date objects:
import ky from "ky";

const kyInstance = ky.create({
parseJson: (text) =>
JSON.parse(text, (key, value) => {
if (key.endsWith("At")) return new Date(value);
return value;
}),
});

export default kyInstance;
import ky from "ky";

const kyInstance = ky.create({
parseJson: (text) =>
JSON.parse(text, (key, value) => {
if (key.endsWith("At")) return new Date(value);
return value;
}),
});

export default kyInstance;
Because when I fetch a prisma object in a server component, I also get a Date back