Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempted to call validateAndNormalizeDocument() from the server [fields-document] #8717

Closed
Thinkscape opened this issue Jul 28, 2023 · 9 comments · Fixed by #9041
Closed

Comments

@Thinkscape
Copy link

Thinkscape commented Jul 28, 2023

Summary

I believe this is a regression after #8403 @borisno2
After adding "use client" to packages/fields-document/src/validation.ts, things break server-side when validation is triggered by admin hooks.

    "@keystone-6/core": "^5.3.2",
    "@keystone-6/fields-document": "^8.0.0",
    "next": "^13.4.12",

Steps

  1. [email protected]+ app using App Router (default next.config.ts).
  2. A schema, including a list, with a field content: document({})
  3. A vanila /api/graphql route, such as this one.
  4. Go to admin
  5. Try to add an item to the list

Expected

I can add a list with document field.

Actual

GraphQLError: An error occured while resolving input fields.
  - Article.content: Attempted to call validateAndNormalizeDocument() from the server but validateAndNormalizeDocument is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.
    at resolverError (webpack-internal:///(sc_server)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_w4vjmt6rg75wwrvq3lhneegeza/node_modules/@keystone-6/core/dist/graphql-errors-473725b1.esm.js:83:12)
    at getResolvedData (webpack-internal:///(sc_server)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_w4vjmt6rg75wwrvq3lhneegeza/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:987:105)
    at async resolveInputForCreateOrUpdate (webpack-internal:///(sc_server)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_w4vjmt6rg75wwrvq3lhneegeza/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:1112:29)
    at async createSingle (webpack-internal:///(sc_server)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_w4vjmt6rg75wwrvq3lhneegeza/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:857:38)
    at async createOne (webpack-internal:///(sc_server)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_w4vjmt6rg75wwrvq3lhneegeza/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:903:38)"
"extensions": {
                "code": "KS_RESOLVER_ERROR",
                "debug": [
                    {
                        "stacktrace": "Error: Attempted to call validateAndNormalizeDocument() from the server but validateAndNormalizeDocument is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.\n    at Object.defineProperties.$$typeof.value (webpack-internal:///(rsc)/./node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected]/node_modules/next/dist/build/webpack/loaders/next-flight-loader/module-proxy.js:151:23)\n    at inputResolver (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@keystone-6/fields-document/dist/keystone-6-fields-document.esm.js:797:109)\n    at resolve (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@keystone-6/fields-document/dist/keystone-6-fields-document.esm.js:834:32)\n    at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:974:31)\n    at Array.map (<anonymous>)\n    at getResolvedData (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:967:86)\n    at resolveInputForCreateOrUpdate (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:1112:35)\n    at createSingle (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:857:44)\n    at async createOne (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:903:38)",
                        "message": "Attempted to call validateAndNormalizeDocument() from the server but validateAndNormalizeDocument is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component."
                    }
                ],
                "stacktrace": [
                    "GraphQLError: An error occured while resolving input fields.",
                    "  - Article.content: Attempted to call validateAndNormalizeDocument() from the server but validateAndNormalizeDocument is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.",
                    "    at resolverError (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/graphql-errors-473725b1.esm.js:83:12)",
                    "    at getResolvedData (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:987:105)",
                    "    at async resolveInputForCreateOrUpdate (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:1112:29)",
                    "    at async createSingle (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:857:38)",
                    "    at async createOne (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@prisma+generator_msm77dva2wee244pexolhg3lh4/node_modules/@keystone-6/core/dist/createSystem-095bd0da.esm.js:903:38)"
                ]
            }
@Thinkscape
Copy link
Author

Workaround

The only workaround I've found right now is to temporarily put the /api/graphql route in the legacy pages folder, i.e.

src/pages/api/graphql.ts

import { ApolloServer } from "@apollo/server";
import { startServerAndCreateNextHandler } from "@as-integrations/next";
import { getContext, getKeystoneSessionContext } from "../../keystone/context";

const apolloServer = new ApolloServer({
  schema: getContext().graphql.schema,
});

export default startServerAndCreateNextHandler(apolloServer, {
  context: async (req, res) => getKeystoneSessionContext({ req, res }),
});

@Thinkscape Thinkscape changed the title Regression: Attempted to call validateAndNormalizeDocument() from the server [fields-document] Attempted to call validateAndNormalizeDocument() from the server [fields-document] Jul 28, 2023
@borisno2
Copy link
Member

Hey @Thinkscape thanks for the info, just to confirm this is only when you use getContext in a custom GraphQL route handler in NextJS?

@Thinkscape
Copy link
Author

Thinkscape commented Aug 1, 2023

Hey @Thinkscape thanks for the info, just to confirm this is only when you use getContext in a custom GraphQL route handler in NextJS?

I don't fully understand what you mean by custom in this context.

My route handler is very similar to yours here, i'm also using @as-integrations/next and a version of:

const handler = startServerAndCreateNextHandler<NextRequest>(apolloServer, {
  context: async () => getServerActionContext(),
})

There's nothing special in the implementation.

It only breaks if I import { document } from "@keystone-6/fields-document", otherwise all works fine with other keystone field types.

@Thinkscape
Copy link
Author

Tried reverting #8403 but then it breaks in other places 🤔 Maybe the validator needs to be split into two different versions? (one of them with "use client", and one meant for server-side)

@borisno2
Copy link
Member

borisno2 commented Aug 2, 2023

Yes, without #8403, it was impossible to use the document field with getContext with Next App Router, so #8403 was an initial step towards this. Still, you are correct, to support mutations via the NextJS App Router the validation needs to be fundamentally changed so that it doesn't require any client-only side code.

@Thinkscape
Copy link
Author

When solving similar problems in my apps, I'd usually extract the logic into a isomorphic lib/component, then import it from either server/client consumers. Quite common for stuff like validation, which works surprisingly smoothly with isomorphic zod schemas 🤔 (I use one schema across react-hook-form, gql mutation handlers, inngest functions etc.)

@mmachatschek
Copy link
Contributor

@borisno2 I was just hit with this issue too, after using app router I'm unable to update fields which use the document KS fields

@mstfash
Copy link

mstfash commented Feb 5, 2024

@Thinkscape @borisno2 I am still having the same issue even tough I have the same code as @borisno2 but still gives me error when creating a new blog post for example, the error is:

An error occurred while resolving input fields.
  - blog.content: Attempted to call validateAndNormalizeDocument() from the server but validateAndNormalizeDocument is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.

That also giving me issue with contentBlocks which might be different matter but still document field is troublesome when used with Keystone-Nextjs monorepo

Do you guys have any solution for that ? Thanks in advance!


Update:

I have worked with pages router since it seems the only thing that works with document field and its customizations and everything works fine, but uploading image while disabling bodyParser of next js gives this weird error

TypeError: RequestInit: duplex option is required when sending a body.

Let me know if you guys have a solution for that! thanks

@mstfash
Copy link

mstfash commented Feb 8, 2024

Well this is weird, using pages router works with document field but messes my custom image upload based on @borisno2 example of vercel/blob but I am using uploadthing instead and upload gives me 2 errors:

1- This happens with graphql-yoga when having bodyParser as false

TypeError: RequestInit: duplex option is required when sending a body.

2- This happens when using @as-integrations instead of graphql-yoga

POST body missing, invalid Content-Type, or JSON object has no keys.

Any thing regarding that ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants