ljonel
ljonel4mo ago

Nextjs server actions does not work with revalidatePath

Hi there! I'm struggling with basic data refreshing on a simple next14 app directory page.tsx. Basically this is all code taken from https://docs.convex.dev/client/react/nextjs/server-rendering#server-actions-and-route-handlers. /src/app/testpage
const page = async () => {
const token = convexAuthNextjsToken();
const workspaces = await fetchQuery(api.queries.workspaces.get, {}, { token });

async function createTask(formData: FormData) {
"use server";

await fetchMutation(
api.queries.workspaces.create,
{
name: formData.get("name") as string,
},
{ token },
);
revalidatePath("/testpage");
}
return (
<div>
<form action={createTask}>
<Input type="text" name="name" />
<Button type="submit">Submit</Button>
</form>

{workspaces?.map((x) => <p>{x.name}</p>)}
</div>
);
};

export default page;
const page = async () => {
const token = convexAuthNextjsToken();
const workspaces = await fetchQuery(api.queries.workspaces.get, {}, { token });

async function createTask(formData: FormData) {
"use server";

await fetchMutation(
api.queries.workspaces.create,
{
name: formData.get("name") as string,
},
{ token },
);
revalidatePath("/testpage");
}
return (
<div>
<form action={createTask}>
<Input type="text" name="name" />
<Button type="submit">Submit</Button>
</form>

{workspaces?.map((x) => <p>{x.name}</p>)}
</div>
);
};

export default page;
Everything works quite well. The workspace is created, but the page data does not refresh when using revalidatePath. I'm wondering what could be causing this problem and does it work for anyone else? And will it be possible to revalidate the queries using the next tags? I can add that I tried using preloadQuery and usePreloadedQuery. Then everything works and refreshes. But it would be nice however to use revalidate method.
5 Replies
Convex Bot
Convex Bot4mo ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
jamwt
jamwt3mo ago
not sure you need to use revalidate and revalidate path with convex can you say more about why that's a benefit? you're trying to avoid the server-side computation if you can? for speed?
ljonel
ljonelOP3mo ago
When I want to keep everything like above getting/creating workspace on server side the data does not refresh. After manually refreshing the page it does, but that's not the point. I'm only giving the example you have in the documentation because I wanted to test it, and I thought it will work on a server with revalidate.
ballingt
ballingt3mo ago
I just tried something similar and this code works for me:
import { api } from "@/convex/_generated/api";
import { fetchMutation, fetchQuery } from "convex/nextjs";
import { revalidatePath } from "next/cache";

export default async function PureServerPage() {
const tasks = await fetchQuery(api.tasks.list, { list: "default" });
async function createTask(formData: FormData) {
"use server";

await fetchMutation(api.tasks.create, {
text: formData.get("text") as string,
});
revalidatePath("/example");
}
// render tasks and task creation form
return (
<div>
<form action={createTask}>
<input type="text" name="text" />
<button type="submit">Update text</button>
</form>
;
<ul>
{tasks.map((t) => {
return <li key={t._id}>{t.text}</li>;
})}
</ul>
</div>
);
}
import { api } from "@/convex/_generated/api";
import { fetchMutation, fetchQuery } from "convex/nextjs";
import { revalidatePath } from "next/cache";

export default async function PureServerPage() {
const tasks = await fetchQuery(api.tasks.list, { list: "default" });
async function createTask(formData: FormData) {
"use server";

await fetchMutation(api.tasks.create, {
text: formData.get("text") as string,
});
revalidatePath("/example");
}
// render tasks and task creation form
return (
<div>
<form action={createTask}>
<input type="text" name="text" />
<button type="submit">Update text</button>
</form>
;
<ul>
{tasks.map((t) => {
return <li key={t._id}>{t.text}</li>;
})}
</ul>
</div>
);
}
Is your file /src/app/testpage/page.tsx? Can you one of these working without Convex? I'm guessing Convex is not the issue here, and there's some Next.js subtlety. What version of Next.js are you using?
ljonel
ljonelOP3mo ago
I am very grateful that you wanted to help. Well after a one day research adventure I am done with it. https://github.com/vercel/next.js/issues/50659 The problem is in the middleware. This was my middleware.
const isPublicPage = createRouteMatcher([“/signin”]);

export default convexAuthNextjsMiddleware((request) => {
if (!isPublicPage(request) && !isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, “/signin”);
}

if (isPublicPage(request) && isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, “/”);
}
});

export const config = {
matcher: [“/((?!!!*|_next).*)”,“/”,“/(api|trpc)(.*)”],
};
const isPublicPage = createRouteMatcher([“/signin”]);

export default convexAuthNextjsMiddleware((request) => {
if (!isPublicPage(request) && !isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, “/signin”);
}

if (isPublicPage(request) && isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, “/”);
}
});

export const config = {
matcher: [“/((?!!!*|_next).*)”,“/”,“/(api|trpc)(.*)”],
};
Unfortunately, here revalidatePath and server actions do not want to work. So I changed the config to
matcher: [
{
source: '/((?!!!*|_next).*)', '/', '/(api|trpc)(.*)',
missing: [{ type: 'header', key: 'next-action' }],
},
],
matcher: [
{
source: '/((?!!!*|_next).*)', '/', '/(api|trpc)(.*)',
missing: [{ type: 'header', key: 'next-action' }],
},
],
Revalidatepath started working, server actions too, but login and logout stopped working. so, with the help of chatgpt, I ended up converting the middleware to something like this
const isPublicPage = createRouteMatcher(["/signin", "/signout"]);

export default convexAuthNextjsMiddleware((request) => {
if (isPublicPage(request) && isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, "/");
}

if (!isPublicPage(request) && !isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, "/signin");
}
});

export const config = {
matcher: [
{
source: "/((?!.*\\..*|_next).*)",
missing: [{ type: "header", key: "next-action" }],
},
"/(api|trpc)(.*)",
],
};
const isPublicPage = createRouteMatcher(["/signin", "/signout"]);

export default convexAuthNextjsMiddleware((request) => {
if (isPublicPage(request) && isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, "/");
}

if (!isPublicPage(request) && !isAuthenticatedNextjs()) {
return nextjsMiddlewareRedirect(request, "/signin");
}
});

export const config = {
matcher: [
{
source: "/((?!.*\\..*|_next).*)",
missing: [{ type: "header", key: "next-action" }],
},
"/(api|trpc)(.*)",
],
};
I've seen that a lot of people have had this problem so maybe this will be useful to someone if they need it

Did you find this page helpful?