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 from
getServerSidePropsin "/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.
mysql2
librarygetServerSideProps
I expect the data to be serialized without an error since JSON.stringify()
works just fine.
Appears others are having this problem: https://stackoverflow.com/questions/61188494/getserversideprops-and-mysql-rowdatapacket
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?
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 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...
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.
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 馃憤
Thank you for the answer.
@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 throw
ing?
Example: https://codesandbox.io/s/httpsgithubcomzeitnextjsissues11993-sumki?file=/pages/index.js:405-681
+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.
@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 tores.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 beforethrow
ing?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.
@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.
Alright, check out https://github.com/zeit/next.js/pull/12156 馃憖
:| 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.
@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).
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:
Credit to this post in StackOverflow
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
}
Most helpful comment
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 theDate
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 馃憤