next.jsreact-hook-formserver-action

Only plain objects can be passed to Server Functions


Nextjs server action issue

in the form:

type Ingredient = {
    name: string;
    purchasePrice: Prisma.Decimal;
}
const onSubmit = async (values: Ingredient) => {
        await addIngredient(values)
    }

in the ingredient-actions.ts file:

export const addIngredient = async (ingredientData: Ingredient ): Promise<IngredientPrisma | ErrorMessage> => {

    try {

        const ingredientResponse = await prisma.ingredient.create({ data: { 
                name: ingredientData.name,
                purchasePrice: ingredientData.purchasePrice,
            } });

        revalidatePath('/dashboard');

        return ingredientResponse;

    } catch (err) {
        return {
            message: 'Error creating ingredient'
        }
    }

}

and I'm having an error ("Only plain objects can be passed to Server Functions"),

so meanwhile I'm "fixing" this by doing addIngredient(JSON.stringify(values)) in the onSubmit. and in the definition of the action, undo by doing JSON.parse(ingredientData).

export const addIngredient = async (ingredientData: string): Promise<IngredientPrisma | ErrorMessage> => {

    try {

        const ingredient:Ingredient = JSON.parse(ingredientData);

        const ingredientResponse = await prisma.ingredient.create({ data: { 
                name: ingredient.name,
                purchasePrice: ingredient.purchasePrice,
            } });

        revalidatePath('/dashboard');

        return ingredientResponse;

    } catch (err) {
        return {
            message: 'Error creating ingredient'
        }
    }

}

how would you deal with that without using JSON ?


Solution

  • Why does this happen?

    As the error indicates, plain objects are allowed without having to convert them to a JSON-string first. That means that stuff like functions are not allowed (since functions cannot be serialized and sent to the server). However a Prisma.Decimal is not a plain object, nor an array nor a primitive (like a string, number or boolean).

    Peeking into the Prisma docs, we find that:

    Decimal fields are represented by the Decimal.js library.

    That library constructs "class" (in quotes because JS doesn't technically have classes and they are just syntax-sugar for prototype-based inheritance) instances which can (and in case of decimal.js do) have instance functions which cannot be serialized.

    What do we do with it?

    First of all, I am not sure if this is a compiler / linter warning or an actual runtime warning. Regardless of whether it's a compiler / linter warning, it might suffice to change the typing of Ingredient to this:

    type Ingredient = {
        name: string;
        purchasePrice: number;
    }
    

    This should also be more accurate since your frontend should not have a "concept" of Prisma.
    The lines between frontend and backend blur a lot with Next.js but since the server-action is called from the frontend, that bit of code should only be concerned with pure data and not a specific "representation" the ORM then deals with it. You can still convert it to a Prisma.Decimal before storing it in the database.