Reason: `object` ("[object Date]") cannot be serialized as JSON. Please only return JSON serializable data types
Asked Answered
S

6

25

I am using Prisma and Next.js. When I try to retrieve the content from Prisma in getStaticProps it does fetch the data but I can't pass it on to the main component.

export const getStaticProps = async () => {
  const prisma = new PrismaClient();
  const newsLetters = await prisma.newsLetters.findMany();
  console.log(newsLetters);

  return {
    props: {
      newsLetters: newsLetters,
    },
  };
};

As you can see in this image it is fetching as well as printing the content.

enter image description here

But when I pass I get the following error for passing it as props

Reason: `object` ("[object Date]") cannot be serialized as JSON. Please only return JSON serializable data types.
Suppository answered 22/12, 2021 at 12:30 Comment(0)
A
8

Looks like nextJS doesn't like serializing anything but scalar types for performance reasons. You can read more in this github issue. Best way you can handle this is that you convert your Date objects to UNIX timestamp before returning them.

// your data
let newsLetters = [
    {
        id: 'your-id',
        email: '[email protected]',
        createdAt: new Date()
    }
];

// map the array
newsLetters.map(x => {
    x.createdAt = Math.floor(x.createdAt / 1000);
    return x;
})

// use newsLetters now
console.log(newsLetters);
Aeschylus answered 22/12, 2021 at 12:43 Comment(1)
If you don't need to edit the date later (probably not with a created at), you can convert it to a legible string instead of a unix timestamp. newsLetters.map(x => { x.createdAt = x.createdAt.toString() return x; })Agrostology
A
30

If you're using TypeScript, you can't change the type of createdAt to a string or number. This won't work:

newsLetter.createdAt = newsLetter.createdAt.toString();
// Error: Type 'string' is not assignable to type 'Date'.

Instead, you can use JSON.stringify inside JSON.parse to create a serializable object:

export const getStaticProps = async () => {
  const prisma = new PrismaClient();
  const newsLetters = await prisma.newsLetters.findMany();

  return {
     props: {
        newsLetters: JSON.parse(JSON.stringify(newsLetters)) // <===
     }
  }
}
Aerostat answered 2/7, 2022 at 7:25 Comment(1)
Not pretty, yet pragmatic. Thank you for this workaround.Expellee
A
11

You can use Blitz's superjson package to get this working. They have instructions at https://github.com/blitz-js/superjson#using-with-nextjs:

Using with Next.js

The getServerSideProps, getInitialProps, and getStaticProps data hooks provided by Next.js do not allow you to transmit Javascript objects like Dates. It will error unless you convert Dates to strings, etc.

Thankfully, Superjson is a perfect tool to bypass that limitation!

Next.js SWC Plugin (experimental, v12.2 or above)

Next.js SWC plugins are experimental, but promise a significant speedup. To use the SuperJSON SWC plugin, install it and add it to your next.config.js:

yarn add next-superjson-plugin
// next.config.js
module.exports = {
  experimental: {
    swcPlugins: [
      [
        'next-superjson-plugin',
        {
          excluded: [],
        },
      ],
    ],
  },
} 
Airsick answered 2/10, 2022 at 20:22 Comment(1)
thanks! had to restart server a couple times before it starts working thoughKnighthood
A
8

Looks like nextJS doesn't like serializing anything but scalar types for performance reasons. You can read more in this github issue. Best way you can handle this is that you convert your Date objects to UNIX timestamp before returning them.

// your data
let newsLetters = [
    {
        id: 'your-id',
        email: '[email protected]',
        createdAt: new Date()
    }
];

// map the array
newsLetters.map(x => {
    x.createdAt = Math.floor(x.createdAt / 1000);
    return x;
})

// use newsLetters now
console.log(newsLetters);
Aeschylus answered 22/12, 2021 at 12:43 Comment(1)
If you don't need to edit the date later (probably not with a created at), you can convert it to a legible string instead of a unix timestamp. newsLetters.map(x => { x.createdAt = x.createdAt.toString() return x; })Agrostology
A
2

According to NextJS API Docs getStaticProps return "should be a serializable object so that any props passed, could be serialized with JSON.stringify."

Under the hood they allow, boolean, number, string, and anything that passes Lodash isPlainObject test. https://lodash.com/docs/#isPlainObjectChecks. In Lodash Documentation for this function it claims "Checks if value is a plain object, that is, an object created by the Object constructor or one with a [[Prototype]] of null."

The Following stack post discusses the difference. The difference between object and plain object in JavaScript?

Building on @Viktor Kynchev answer, depending on what your needing from the prop you can just convert it to a string, or number or some other type accepted by Lodash's isPlainObject.

For me I had a date Object provided via Prisma API just like OP, and I just converted it to a string like so.

for (const element of newsLetters) {
  element.createdAt = element.createdAt.toString()
}
Agrostology answered 31/3, 2022 at 18:49 Comment(3)
the conversion snippet you proposed doesn't work since element.createdAt remains of type DateTime, and you can't assign a string to itMak
@Mak , its working for me, I needed it so frequently, I put it in a helper function and used it repeatedly. In what context did it not work for you? Perhaps the DateTime object you were working with was different?Agrostology
@GreggoryWiley I think L0g1x is using typescriptPerspicacity
C
1

You probably have this in one of your model fields

createdAt DateTime @default(now())

date objects are not serializable. you could use date-fns npm module

import { formatDistance } from 'date-fns'

const newsLetters = await prisma.newsLetters.findMany();
const serializedNesLetters= newsLetters.map((newsLetter)=>({
     ...newsLetter, 
     createdAt:formatDistance(new Date(newsLetter.timestamp),new Date())
}))

this will return the distance between the given dates in words.

formatDistance docs

Claret answered 14/4, 2023 at 4:28 Comment(0)
P
0

Transform your Date value to some String value, like this:


export async function getStaticProps() {
  const storeInfo = await getStoreInfo()

  return {
    props: {
      storeInfo: {
        ...storeInfo,
        createdAt: storeInfo?.createdAt.toISOString(),
        updatedAt: storeInfo?.updatedAt.toISOString(),
      },
    },
  }
}
Petrochemistry answered 13/6, 2023 at 2:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.