next.js: getServerSideProps cannot be serialized as JSON. Please only return JSON serializable data types

_Edit from @leerob: See updated answer: https://github.com/vercel/next.js/issues/11993#issuecomment-1504415523_


Bug report

Describe the bug

I’m returning a row set from MySQL (using the mysql2 library) in my getServerSideProps. Getting the following error:

SerializableError: Error serializing .data[0]returned fromgetServerSidePropsin "/reports/[report]/[start]/[end]". Reason:object ("[object Object]") cannot be serialized as JSON. Please only return JSON serializable data types.

But calling JSON.stringify(data) works just fine.

If I do a JSON.parse(JSON.stringify(data)) it works just fine, but that seems very odd.

To Reproduce

  1. Execute a query using the mysql2 library
  2. Return the row set as a prop in getServerSideProps
  3. Get Error.

Expected behavior

I expect the data to be serialized without an error since JSON.stringify() works just fine.

Additional context

Appears others are having this problem: https://stackoverflow.com/questions/61188494/getserversideprops-and-mysql-rowdatapacket

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 61
  • Comments: 32 (6 by maintainers)

Commits related to this issue

Most upvoted comments

The main reasoning behind this limitation is that with getInitialProps we used to allow anything that can be stringified and that resulted in some really hard to track down UX bugs when hydrating client-side. Eg the hydration would fail because suddenly the Date object you had server-side is a string client-side.

We need to serialize the result of getStaticProps/getServerSideProps because it has to be sent to the browser to do hydration.

So we basically went with a strict checking mechanism (only runs in development) in order to prevent this from happening. We can potentially add an option to bypass in the future if there’s enough need for it 👍

@timneutkens I don’t get why this issue is closed. It seems quite a lot of people are still stumbling upon this and there is demand for a better solution. A warning in development mode seems to be sufficient to help users track down hydration errors and it wouldn’t require everyone to use hacks to serialize dates and such.

I have the exact same problem with mongoose. I thought it was because of the ObjectId format, but even if I “stringify” the _id property before it gets sent to getServerSideProps() I get the same error.

I’ve also had the idea to use JSON.parse(JSON.stringify(data)) and it works. But it’s a silly solution if you ask me…

Any news on the PR?

For people using mongoose, I solved it using the .lean() method to convert in a plain javascript object and removing the object Id from the query as it follows: image

Credit to this post in StackOverflow

@timneutkens We’re on the same page.

My proposal is to automatically handle serialization via .toJSON.

I’ll open a PR for it, since code > words.

+1 for an option to bypass.

I’ve disabled all type casting in mysql2 but the objects returned are still of type TextRow even though they are just plain javascript objects consisting of nothing but primitives. I have to do rows.map(row => ({...row})) to keep from triggering that error.

Given that the primary use case of this feature is to fetch data from some data store and shove it into props, I think this will become quite a pain.

Even if it was just a warning and not a hard error it could still be helpful in development.

if anyone works with axios, the object returned from the api call is in data field, so if all the response of the axios is passed, next will alert that it’s not serializable and function isn’t allowed. my solution:

export async function getServerSideProps() {
    const { data: products } = await axios.get(
        `${SERVER_URL}\\products`);
    return { props: { products } };
}

const productsPage = ({ products }) => {
 \\do something with products
}

Newbie here to NextJS. It seems strange, especially since querying directly from a PostgreSQL db (via Prisma) usually brings up createdAt and updatedAt and other date fields.

For now, doing what @jeromemeichelbeck recommends by parsing results through a converter:

const dateStripped = obj => {
	let newObj = {}
	Object.keys(obj).forEach(key => {
		let value = obj[key]
		if (value !== null) {
			// If array, loop...
			if (Array.isArray(value)) {
				value = value.map(item => dateStripped(item))
			}
			// ...if property is date/time, stringify/parse...
			else if (typeof value === 'object' && typeof value.getMonth === 'function') {
				value = JSON.parse(JSON.stringify(value))
			}
			// ...and if a deep object, loop.
			else if (typeof value === 'object') {
				value = dateStripped(value)
			}
		}
		newObj[key] = value
	})
	return newObj
}

Hi Folks. I think this was a red herring. I’m using the mysql2 library and I did not realize that it is doing some magic conversions behind the scenes to convert dates and some other mysql types to native javascript objects that cannot be serialized without conversion. JSON.stingify() attempts to convert these automatically but it appears Next.JS is using a different serialization method that does not do this conversion.

Now that I know more about what’s going on I think the approach the Next.JS team is taking is the correct one. It’s better to be explicit and give the developer full control than to do magical conversions that may or may not be surprising. Maybe the Next.JS folks could add a “strict” configuration that allows people to opt in to the JSON.stringify() behavior?

For those of you still dealing with this you can continue with the JSON.parse(JSON.stringify) hack or you can make sure your data consists of easily-serializable primitives.

I’m closing this issue now as it appears to be working as designed and is not a bug. If someone wants to open a feature request go for it.

I stumbled across a similar problem and got it working with by using the superjson-next plugin. See more here: https://github.com/blitz-js/superjson#using-with-nextjs

@timneutkens @chrisbrantley I came across this recently while working on Next.js support for Amplify JS.

This throws an error (relevant code):

// pages/index.tsx
export async function getServerSideProps(context) {
	const posts = await DataStore.query(Post);
   // [Post {...}, Post {...}, ...]

	return {
		props: {
			posts
		},
	};
}

Whereas our /api/posts route works (due to res.json):

// pages/api/posts.tsx
export default async (req: NextApiRequest, res: NextApiResponse) => {
  try {
    const posts = await DataStore.query(Post);

    return res.status(200).json(posts)
  } catch (error) {
    console.error(error)
    return res.status(500).json({ error })
  }
}

I understand the undesirable impact of using JSON.stringify, but would you be open to a PR that checked for .toJSON as a final check/casting before throwing?

https://github.com/zeit/next.js/blob/a4ab0887465d43ad3081e142924145a495b5727f/packages/next/lib/is-serializable-props.ts#L118-L132

Example: https://codesandbox.io/s/httpsgithubcomzeitnextjsissues11993-sumki?file=/pages/index.js:405-681

😐 After hours to think this ~ I fixed this 😃 Some data in query return is object like mongoID and it make JSON.parse not work. Yah I know I so nood. image

This is my solution for Prisma. Maybe this is a better way to show what the problem is.


export const getServerSideProps: GetServerSideProps<PostProps> = async () => {

    const posts = await prisma.post.findMany();

    return {

        props: {

            posts: posts.map((post: post) => ({

                ...post,

                created_at: post.updated_at.toISOString(),

                updated_at: post.updated_at.toISOString(),

                deleted_at: post.deleted_at === null ? post.deleted_at : post.deleted_at.toISOString(),

            } as unknown as post)),

        }

    };

};

This is solution https://github.com/blitz-js/superjson#using-with-nextjs

Superjson worked for me

the _id and _v0 are object types. So according to MongoDB docs, we need to call a toString() method on these properties to get string values

i am getting data from firestore. faced the same issue because of timestamp in my response. i converted the result from firestore into a string with JSON.stringify() and then again from my props parsed it. working fine

@timneutkens @chrisbrantley I came across this recently while working on Next.js support for Amplify JS.

This throws an error (relevant code):

// pages/index.tsx
export async function getServerSideProps(context) {
	const posts = await DataStore.query(Post);
   // [Post {...}, Post {...}, ...]

	return {
		props: {
			posts
		},
	};
}

Whereas our /api/posts route works (due to res.json):

// pages/api/posts.tsx
export default async (req: NextApiRequest, res: NextApiResponse) => {
  try {
    const posts = await DataStore.query(Post);

    return res.status(200).json(posts)
  } catch (error) {
    console.error(error)
    return res.status(500).json({ error })
  }
}

I understand the undesirable impact of using JSON.stringify, but would you be open to a PR that checked for .toJSON as a final check/casting before throwing?

https://github.com/zeit/next.js/blob/a4ab0887465d43ad3081e142924145a495b5727f/packages/next/lib/is-serializable-props.ts#L118-L132

Example: codesandbox.io/s/httpsgithubcomzeitnextjsissues11993-sumki?file=/pages/index.js:405-681

In API routes it’s expected that you’re casting the values to JSON and they’re JSON client-side. With getStaticProps/getServerSideProps that is not the case and caused really tricky to debug hydration issues (we’ve seen these in production Next.js applications). As said I’m not opposed to adding an explicit option to bypass but the default behavior prevents production issues down the line.

Hopefully this helps someone struggling like me with the issue.

const posts = await model.find().lean();
	return {
		props: {
			posts: JSON.parse(JSON.stringify(posts)),
		},
	};
}

Mongo + Next:

_id property name and other non-string property values throw this error

DB method

export function getFeed(db, urlSlug) {
  return db
    .collection('feeds')
    .findOne({
      slug: urlSlug,
    })
    .then((feed) => serialize(feed))
}

Helper func to serialize data once it’s returned from the db method

export async function serialize({ _id, image, title, slug, details, subtitle, author, avatar, article, createdAt }) {
  return {
    id: _id.toString(), // for some reason next doesn't like the `_id` property + non-string
    image: image,
    title: title,
    slug: slug,
    details: details,
    subtitle: subtitle,
    author: author,
    avatar: avatar,
    article: article,
    createdAt: createdAt.toString(), // cast as string
  }
}

I had the same issue today with objects returned from a mysql2 query.

This can be resolved without converting the result to json and back again by either using object assign or the spread operator.

So instead of return myRetrievedObject;, you can do return Object.assign({}, myRetrievedObject); or return {...myRetrievedObject};.

Hope this helps!

if anyone here is getting this warning with mongo, you need to supress the _id field bc json does not support hex https://stackoverflow.com/a/52250461/1426788

@warcraft14115 I have a PR to fix it, but there were some rogue file-size tests that got it out of date.

I’ll have to get https://github.com/zeit/next.js/pull/12156 passing again and approved (I don’t think anyone has looked at it yet).

Update: while it makes sense that passing an Object (like a Timeslot) would cause errors (because method’s can’t be serialized), I’ve implemented toJSON() and fromJSON() methods that should make this work (but don’t):

public toJSON(): TimeslotInterface {
  return { from: this.from, to: this.to };
}
public static fromJSON(timeslot: TimeslotInterface): Timeslot {
  return new Timeslot(new Date(timeslot.from), new Date(timeslot.to));
}

Still doesn’t work.

I’m having the same problem with a custom Timeslot object that’s pretty straight forward:

interface TimeslotInterface {
  from: Date;
  to: Date;
}
class Timeslot implements TimeslotInterface {
  public constructor(public from: Date, public to: Date) {}
  public toString(): string {
    return this.from.toLocaleTimeString() + ' - ' + this.to.toLocaleTimeString();
  }
}

Same as above, JSON.stringify() works just fine, but Next.js seems to be throwing the same error:

Error serializing `.filters.availability[0]` returned from `getServerSideProps` in "/search". Reason: `object` ("[object Object]") cannot be serialized as JSON. Please only return JSON serializable data types.

Any suggestions?