Fixing Issue with NextJS and Prisma Date Types

Chris Board

Aug 17, 20224 min read
developmentreactjsnextjsprismaweb development

I recently started working on NextJS (second attempt, I tried it once before but didn't like it - I did a post on IndieHackers about my experience). I thought I must have missed something as NextJS is so popular and see so many people saying how good it is, so I decided to give another try - and so far so good. Except for one issue, with NextJS and PrismaJS returning Date objects not being able to be serialized as JSON.

If you aren't aware Prisma is an ORM to perform CRUD operations on various databases. For my project I am using MySQL. I ran into a problem where a table model in Prisma has a DateTime field and when I return this in the getStaticProps I receive an error such as below.

Error From NextJS

As you see in the above, there is an issue when I return posts from Prisma due to createdAt not being seralizable.

Currently, my code in getStaticProps is as follows

export const getStaticProps = async () => {
    try
    {
        const posts = await prismaClient.posts.findMany({
            include: {
                author: true
            }
        });

        return {
            props: {
                posts: posts
            },
            revalidate: 60
        }
    }
    catch (err)
    {
        return {
            props: {
                err: err.toString()
            }
        }
    }
}

And my model of posts and users (where author is being included) is below:

model users {
  userId        Int         @id @default(autoincrement())
  registeredAt  DateTime   @default(now())
  firstName     String      @db.VarChar(250)
  lastName      String      @db.VarChar(250)
  email         String      @db.VarChar(250)
  password      String      @db.VarChar(250)
  isActive      String      @default("1") @db.Char(1)
  posts         posts[]
}

model posts {
    id          Int         @id @default(autoincrement())
    createdAt   DateTime    @default(now())
    title       String      @db.VarChar(255)
    content     String      @db.MediumText
    slug        String      @db.VarChar(255)
    published   Boolean     @default(false)
    author      users       @relation(fields: [authorId], references: [userId])
    authorId    Int
}

As you can see, both posts and users have a DateTime field which will cause this error.

I couldn't find a specific way of fixing it properly, the only suggestion was to use Int and put in an epoch time, but then you can use the database auto timestamp when the record is created/updated.

Therefore I created this method to take the Prisma and convert the Date object into a valid date/time string as below:

export function sanitisePrismaObject (data: any) {
    if (Array.isArray(data))
    {
        for (let i = 0; i < data.length; i++)
        {
            const keys = Object.keys(data[i]);
            keys.forEach(key => {
                if (data[i][key] instanceof Date)
                {
                    data[i][key] = convertDateObjectToString(data[i][key]);
                }
                else if (typeof data[i][key] === "object")
                {
                    sanitisePrismaObject(data[i][key]);
                }
            })
        }
    }
    else
    {
        const keys = Object.keys(data);
        keys.forEach(key => {
            if (data[key] instanceof Date)
            {
                data[key] = convertDateObjectToString(data[key]);
            }
            else if (typeof data[key] === typeof Object)
            {
                sanitisePrismaObject(data[key]);
            }
        });
    }
}

My convertDateObjectToString is as simple as follows and can be changed for your needs

export function convertDateObjectToString(dateTimeString: Date)
{
    const year = dateTimeString.getFullYear();
    const month = (dateTimeString.getMonth()+1).toString()
    	.padStart(2, "0");
    const day = dateTimeString.getDate().toString()
    	.padStart(2, "0");
    const hours = dateTimeString.getHours().toString()
    	.padStart(2, "0");
    const minutes = dateTimeString.getMinutes().toString()
    	.padStart(2, "0");
    const seconds = dateTimeString.getSeconds().toString()
    	.padStart(2, "0");
        
    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

It looks a little horrible above, but its just returning a string of each of the date/month/year and hours/minute components of the date and padding them out to be 2 digits in length.

Then in my getStaticProps I just call await sanitisePrismaObject(posts) and the date is fixed, full example of the getStaticProps is below

export const getStaticProps = async () => {
    try
    {
        const posts = await prismaClient.posts.findMany({
            include: {
                author: true
            }
        });
        await sanitisePrismaObject(posts);

        return {
            props: {
                posts: posts
            },
            revalidate: 60
        }
    }
    catch (err)
    {
        return {
            props: {
                err: err.toString()
            }
        }
    }
}

If you have a better fix than this, then let me know in the comments below.

Chris Board

Test Track

Are you a developer or involved in Quality Assurance Testing or User Acceptance Testing, you might be interested in Test Track

A simple and affordable test planning and management solution.