Warning: Only plain objects can be passed to Client Components from Server Components.
Unanswered
Brown bear posted this in #help-forum
Brown bearOP
Hello, is there any definitive solution to solve this error? I have it in absolutely the entire frontend of my application where I have to execute a server action to make a call to mongoDB (I'm almost sure it's because of the mongo objects.)
Here is an example:
Here is an example:
const ProductColors = async () => {
const { isLoggedIn, user } = await validateRequest()
if (!isLoggedIn ?? !user?.is_admin) return redirect("/login")
const [res, err] = await getAllColors()
if (err) {
console.error(err)
return null
}
return <ColorsTable colors={res} />
}
export default ProductColors
120 Replies
American black bear
That happens when you pass a object that contains a none Primitive Object to a Client like a Date element the best way i found is to create a function to Format the Object into a basic JSON object
Brown bearOP
The issues is with the ids of the objects that i pass
they are schema.objectID
Server actions are merely rest api calls. You have to return something you can return via rest.
Brown bearOP
im returning just gets and posts to the api with endpoints like this
export const getStyles = async () => {
await clientPromise
return await Style.find()
}
and im having the error here:
export const getStyles = async () => {
await clientPromise
return await Style.find()
}
and im having the error here:
import { getStyles } from "@/actions/product.style"
import StylesGrid from "@/components/organisms/styles_grid/styles_grid.component"
import Section from "../section/section.component"
const FeaturedStyles = async ({ explore }: { explore?: boolean }) => {
const [res, err] = await getStyles()
if (err) {
console.error(err)
return null
}
return (
<Section
id="FEATURED_STYLES"
title="Estilos Destacados"
action={{ label: "Ver mas", href: "/explore/styles" }}
>
<StylesGrid styles={res} explore={explore} />
</Section>
)
}
export default FeaturedStyles
and everywhere i use a call to the api
American black bear
Does the res or err object contains anything that is not a string,number,boolean or a other Object based on primitive types?
it's hard to guess why it acts like that, because we don't know what
Style.find()
returnsBrown bearOP
[
{
_id: new ObjectId('666370f8d460bed09df50a49'),
name: 'TestNew',
images: [
'/styles/hype.jpg'
],
__v: 0
},
{
_id: new ObjectId('6663719dd460bed09df50a67'),
name: 'Hype',
images: [
'/styles/hype.jpg'
],
__v: 0
},
{
_id: new ObjectId('666371a0d460bed09df50a69'),
name: 'Streetwear',
images: [
'/styles/hype.jpg'
],
__v: 0
},
{
_id: new ObjectId('666371aad460bed09df50a6b'),
name: 'Old Money',
images: [
'/styles/hype.jpg'
],
__v: 0
},
{
_id: new ObjectId('66883b79a92e768c430a26eb'),
name: 'Creado',
images: [
'/abrigos.jpg',
'/calzado.jpg'
],
__v: 0
}
] STYLES
thats the return of styles
new ObjectId and the end of it contains STYLES
American black bear
You need to transform the ObjectID element to a pure string
Before giving it to the child element
@necm1 new ObjectId and the end of it contains STYLES
Brown bearOP
that "STYLES" is the console.log(styles, "STYLES")
@American black bear You need to transform the ObjectID element to a pure string
shouldn't JSON.stringify() solve it
@American black bear You need to transform the ObjectID element to a pure string
Brown bearOP
but whats the best practice to do that? i found many "solutions" like do something like this:
const stylesPlain = JSON.parse(JSON.stringify(res))
const stylesPlain = JSON.parse(JSON.stringify(res))
Brown bearOP
is that a good practice to do? i have to put that everywhere in all the app?
and whats happens for example if i need to do a create/delete action
where does Styles.find() come from
@necm1 where does Styles.find() come from
Brown bearOP
from actions
American black bear
styles.map({_id, ...props} => ({ _id: _id.toString(), ...props}))
@Brown bear from actions
yea, but I mean how does it get the response
@American black bear styles.map({_id, ...props} => ({ _id: _id.toString(), ...props}))
that could be an idea, but I guess there was a more proper way with transformers
@American black bear styles.map({_id, ...props} => ({ _id: _id.toString(), ...props}))
Brown bearOP
using that in the db?
American black bear
no in the getStyle function
@Brown bear using that in the db?
can you show us the
Style.find()
there is a simpler approach
are you using mongoose?
Brown bearOP
export const getStyles = createServerAction().handler(async () => {
return await db.getStyles()
})
Thats the function that i execute on actions (I'm using SZA) that calls the db.getStyles that do this:
export const getStyles = async () => {
await clientPromise
return await Style.find()
}
i have an actions folder that executes ZSA calling to the DB
and where is the call to the database
@necm1 .
Brown bearOP
yes
alright
could you show me the
db.getStyles()
functionAmerican black bear
export const getStyles = createServerAction().handler(async () => {
const styles = await db.getStyles();
return styles.map({_id, ...props} => ({ _id: _id.toString(), ...props})),
})
mongoose offers
.toObject()
or even .toJson()
for instancesBrown bearOP
export const getStyles = async () => {
await clientPromise
return await Style.find()
}
Thats the getStyles function that calls de mongoDB
and Style.find() is linked to a mongoose entity / document?
Brown bearOP
yes from a Style Model
so in this case
Brown bearOP
export type Style = {
_id: string
name: string
images: string[]
parent_id?: Schema.Types.ObjectId
}
thats the style
return (await Styles.find()).map((style) => style.toObject());
you could also do smth lke
@necm1 typescript
return (await Styles.find()).map((style) => style.toObject());
Brown bearOP
trying this
await Styles.find().lean();
leads to the same
@necm1 typescript
await Styles.find().lean();
would prefer this for more readability
Brown bearOP
having same error with both changes
I don't know how
Styles
looks likeBrown bearOP
it only works if in the front i use this:
const stylesPlain = JSON.parse(JSON.stringify(res))
could you post
Styles
but that seems to be a normal type, not a mongoose document
Brown bearOP
export const styleSchema = new Schema<Style>({
name: { type: String, required: true },
images: { type: [String], required: true }
})
const Style: Model<Style> = models.Style || model<Style>("Style", styleSchema)
thats the model
and where does "Styles" come from
Brown bearOP
export type Style = {
_id: string
name: string
images: string[]
parent_id?: Schema.Types.ObjectId
}
From that one
Brown bearOP
export const getStyles = async () => {
await clientPromise
return await Style.find().lean()
}
@Brown bear
export type Style = {
_id: string
name: string
images: string[]
parent_id?: Schema.Types.ObjectId
}
From that one
no, that type called
Style
(singular) but you're using Styles
(plural)oh nvm
mb
Brown bearOP
yes I use them singular
Brown bearOP
Styles doesnt exist
its Style
import { Style } from "@/models/product.model"
yea
but my code was using
Styles
before instead of Style
@necm1 typescript
await Styles.find().lean();
thats why I asked you if you changed my code to
Style.find().lean()
instead of StyleS.find().lean()
Brown bearOP
ahh ok
yes but with lean I have same error btw
may you post the error?
Brown bearOP
i have 5 styles
and when i enter the page it give me 5 times this error
return (await Style.find()).lean();
Brown bearOP
Warning: Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.
{_id: {buffer: ...}, name: "TestNew", images: ..., __v: ...}
^^^^^^^^^^^^^
Warning: Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.
{_id: {buffer: ...}, name: "Hype", images: ..., __v: ...}
^^^^^^^^^^^^^
Warning: Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.
{_id: {buffer: ...}, name: ..., images: ..., __v: ...}
^^^^^^^^^^^^^
Warning: Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.
{_id: {buffer: ...}, name: ..., images: ..., __v: ...}
^^^^^^^^^^^^^
Warning: Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.
{_id: {buffer: ...}, name: "Creado", images: ..., __v: ...}
@necm1 typescript
return (await Style.find()).lean();
Brown bearOP
this is wrong
Property 'lean' does not exist on type '(Document<unknown, {}, Style> & Style & Required<{ _id: string; }>)[]'.ts(2339)
isn't should be
export const getStyles = async () => {
await clientPromise
const styles = await Style.find().lean()
console.log(styles, "STYLES")
return styles
}
oh yea it seems like the method chaining seems to be inside the await find().lean()
Brown bearOP
this returns the console.log
[
{
_id: new ObjectId('666370f8d460bed09df50a49'),
name: 'TestNew',
images: [
'/styles/hype.jpg'
],
__v: 0
},
{
_id: new ObjectId('6663719dd460bed09df50a67'),
name: 'Hype',
images: [
'/styles/hype.jpg'
],
__v: 0
},
{
_id: new ObjectId('666371a0d460bed09df50a69'),
name: 'Streetwear',
images: [
'/styles/hype.jpg'
],
__v: 0
},
{
_id: new ObjectId('666371aad460bed09df50a6b'),
name: 'Old Money',
images: [
'/styles/hype.jpg'
],
__v: 0
},
{
_id: new ObjectId('66883b79a92e768c430a26eb'),
name: 'Creado',
images: [
'/abrigos.jpg',
'/calzado.jpg'
],
__v: 0
}
] STYLES
so actually
Brown bearOP
chatgpt recommends to do this that is working:
export const getStyles = async () => {
await clientPromise
const styles = await Style.find().lean()
const plainStyles = styles.map((style) => ({
...style,
_id: style._id.toString()
}))
console.log(plainStyles, "STYLES")
return plainStyles
}
but _id is already a plain string
oh no
Brown bearOP
no, its an objectid
_id: new ObjectId('66883b79a92e768c430a26eb'),
_id: new ObjectId('66883b79a92e768c430a26eb'),
yea
so there are multiple ways
1. Map and convert
2. Use a plugin / package like
3. Overwrite
_id
to a normal string2. Use a plugin / package like
mongoose-lean-id
(https://github.com/mongoosejs/mongoose-lean-id)3. Overwrite
toObject()
& toJson()
from your schemadepending on where you have your mongoose import
// global mongoose file
mongoose.set('toObject', {
transform: (doc, ret) => {
ret._id = ret._id.toString();
return ret;
},
});
mongoose.set('toJSON', {
transform: (doc, ret) => {
ret._id = ret._id.toString();
return ret;
},
});
if you only want that for your schema, you could go for this one
styleSchema.set('toObject', {
transform: (doc, ret) => {
ret._id = ret._id.toString();
return ret;
},
});
styleSchema.set('toJSON', {
transform: (doc, ret) => {
ret._id = ret._id.toString();
return ret;
},
});
if you overwrite the transformation in any way, you can use
await Style.find().lean()
which will use the transform
of your schema / globalwould suggest the global transforming
Brown bearOP
ok im taking a look
Original message was deleted
When it worked, could you mark my answer as described here:
Brown bearOP
for sure
@Brown bear for sure
Hi! What's the state? Did it work?
Brown bearOP
Hello
We are doing some tests with your advices and other tries
We are trying to find a global solution to avoid doing changes o each function
Brown bearOP
we created a wrapper for all function that will be executed before any db call that in the middle executes this:
const toPlain = <T>(data: T) => {
if (!data) return data
if (
typeof data === "object" &&
"toObject" in data &&
typeof data.toObject === "function"
) {
return data.toObject({ flattenObjectIds: true })
}
return JSON.parse(JSON.stringify(data))
}