Is it okay to call a server action directly from a useQuery or a useMutation hook? (with example)

I know that in app router you can directly call the server function, but if I have a interactive client component, is it okay to call the server action from a useQuery or useMutation hook? Because this is not one of the ways they speak about in the documentation: https://nextjs.org/docs/app/api-reference/functions/server-actions#invocation
// actions.ts

"use server";

export const getPosts = async () => {
// fetch from db
const posts = [{ id: 1, name: "Hello World" }];
return posts;
};
// actions.ts

"use server";

export const getPosts = async () => {
// fetch from db
const posts = [{ id: 1, name: "Hello World" }];
return posts;
};
// page.tsx

"use client";

import { useQuery } from "@tanstack/react-query";
import { getPosts } from "./actions";

export default function Home() {
const { data, error, isLoading } = useQuery({
queryKey: ["posts"],
queryFn: getPosts,
});

return error ? (
<div>Oops, an error</div>
) : isLoading ? (
<div>Loading...</div>
) : (
<ul>
{data.map((post) => (
<li key={post.id}>{post.name}</li>
))}
</ul>
);
}
// page.tsx

"use client";

import { useQuery } from "@tanstack/react-query";
import { getPosts } from "./actions";

export default function Home() {
const { data, error, isLoading } = useQuery({
queryKey: ["posts"],
queryFn: getPosts,
});

return error ? (
<div>Oops, an error</div>
) : isLoading ? (
<div>Loading...</div>
) : (
<ul>
{data.map((post) => (
<li key={post.id}>{post.name}</li>
))}
</ul>
);
}
1 Reply
ATOMowy_grzyb
ATOMowy_grzyb11mo ago
I think server actions are being abused. They have a specific use case and it's not that you should replace every fetch with a server action, but that seems what everyone is trying to do. Server action is basically Next creating a POST handler from your server-side function, that's why you can plug it into <form action directly - it's a POST endpoint, exactly what a form needs for its action. You'd be better off if you put that DB logic in an API route and you used fetch from the client explicitly. Otherwise you're depending on Next to create that link for you, implicitly (that is what happens when you import a server function into a client component, because the only way the client can talk to the server is over the air, there's no magic there). At least then you can see with your own eyes that you have created a public endpoint that everyone can access and that you have to apply some input validation to it, or see is the one who's sending the request is allowed to do it. Depending on Next to create that for you and potentially thinking "I'm just calling my own function, this must be totally safe" is where I suspect a lot of new vulnerabilities will be born from, because barely anyone thinks of this RPC that it's happening over HTTP on a public URL.
Want results from more Discord servers?
Add your server