Server Action to replace a GET API request
Answered
Upland Sandpiper posted this in #help-forum
Upland SandpiperOP
Hello there.
I'm wondering how to replace a fetch API of GET type request by a server action.
All the examples I saw are to replace a POST or UPDATE API request.
Thanks 🙂
I'm wondering how to replace a fetch API of GET type request by a server action.
All the examples I saw are to replace a POST or UPDATE API request.
Thanks 🙂
Answered by aardani
export default async function Page(){
const data = await getData()
return <ClientComponent data={data} />
}43 Replies
server action mostly for POST mutation request
you can fetch the api directly inside the server component so you don't need to create a endpoint for the GET request anymore
you can use server action to fetch data anyway
Upland SandpiperOP
Do you have example please ?
@Upland Sandpiper Do you have example please ?
const data = await getData()Upland SandpiperOP
So we can't use it in a client component because of the async/await ?
Upland SandpiperOP
Client components cannot be async functions.
"use client";
import Link from "next/link";
import { ChevronRight, HomeIcon } from "lucide-react";
import prisma from "@/lib/prismadb";
import { Category } from "@prisma/client";
interface getBreadcrumbByIdCatParams {
categoryId: string;
}
async function getBreadcrumbByIdCat(params: getBreadcrumbByIdCatParams) {
"use server";
try {
const { categoryId } = params;
const catIdAsNumber = categoryId ? parseInt(categoryId, 10) : undefined;
// on vérifie que le paramètre categoryId existe dans la base de données
const category = await prisma.category.findUnique({
where: { id: catIdAsNumber },
});
if (!category) {
return {
error: `Le paramètre spécifié categoryId : ${categoryId} n'existe pas !`,
};
}
const breadcrumbArray: Category[] = [];
let currentCategoryId: number | undefined = catIdAsNumber;
while (currentCategoryId !== undefined) {
const category: Category | null = await prisma.category.findUnique({
where: { id: currentCategoryId },
select: { id: true, name: true, slug: true, parentId: true },
});
if (category) {
breadcrumbArray.unshift(category);
currentCategoryId = category.parentId ?? undefined;
} else {
break;
}
}
return { data: breadcrumbArray };
} catch (error) {
// Gestion des erreurs imprévues
return { error: "Une erreur inattendue est survenue." };
}
}and here is my client component :
export default async function Breadcrumb() {
// Récupération des données de breadcrumb via un server action
const result = await getBreadcrumbByIdCat({
categoryId: categoryId.toString(),
});
const breadcrumbs = result.data;
return (
<nav className="my-5">
{!breadcrumbs ? (
<div>Aucun chemin trouvé.</div>
) : (
<ul className="flex items-center">
<li>
<Link href="/" className="flex items-center">
<HomeIcon size={15} />
<ChevronRight size={15} className="mx-1" />
</Link>
</li>
{breadcrumbs.map((crumb, index) => (
<li key={crumb.id} className="flex items-center">
{index > 0 && <ChevronRight size={15} className="mx-1" />}
<Link
className="hover:text-primary"
href={`/modele/${crumb.id}-${crumb.slug}`}
>
<span>{crumb.name}</span>
</Link>
</li>
))}
{product && (
<li className="flex items-center">
{breadcrumbs.length > 0 && (
<ChevronRight size={15} className="mx-1" />
)}
<Link
className="hover:text-primary"
href={`/${product.id}-${product.slug}`}
>
<span>{product.name}</span>
</Link>
</li>
)}
</ul>
)}
</nav>
);
}all in the same page
@Upland Sandpiper Client components cannot be async functions.
your code is really messy
client compoent cannot be async. but you can use async function inside client components
"use server" can't be inside of "use client"
Upland SandpiperOP
sorry but "use server" is inside :
async function getBreadcrumbByIdCat(params: getBreadcrumbByIdCatParams) {
"use server";
"use server";
separate it to another file
sorry
Upland SandpiperOP
ok please consider this :
Am I right ?
"use client";
import { usePathname } from "next/navigation";
import { findCategoryBySlug } from "@/lib/actions/category.action";
export default async function MyClientComponent() {
// get the current pathname
const pathname = usePathname();
// get the category id from the pathname with a server action
const category = await findCategoryBySlug(pathname);
const categoryId = category.id;
return <h1>{categoryId}</h1>;
}Am I right ?
just remove "use client"
im not sure what you want
but you can use server action to replace GET API request.
just call server action and return the value
you can call server action
onClick or on formActionor even inside useEffects
Upland SandpiperOP
but I have to put "use client" because I use "usePathname"
well
you can just hardcode the pathname? it wont change right
just pass down the slug from page.jsx
i dont know why you are over complicating it
Upland SandpiperOP
I have to get the id included in the current URL
here is my exported server action :
export async function findCategoryBySlug(catSlug: string) {
try {
// on récupère le slug de la catégorie
// celui-ci contient un id
// Sépare la chaîne au tiret et prend le premier élément
const catId = catSlug.split("-")[0];
// on a maintenant l'id de la catégorie de l'url
// on doit vérifier :
// 1) qu'il s'agit bien d'un nombre
let categoryId = null;
// si catId est null ou undefined
if (!catId) {
return null;
} else {
categoryId = parseInt(catId, 10);
if (isNaN(categoryId)) {
return null;
}
}
// 2) que la catégorie existe bien
const category = await prisma.category.findUnique({
where: { id: categoryId },
select: {
id: true,
name: true,
slug: true,
parentId: true,
},
});
return category;
} catch (error) {
console.log(error);
throw error;
}
}Upland SandpiperOP
ok I will use useEffect to launch my server action without await
Upland SandpiperOP
maybe it can be less messy for you now :
"use client";
import { usePathname } from "next/navigation";
import { findCategoryBySlug } from "@/lib/actions/category.action";
import { useEffect, useState } from "react";
export default function MyClientComponent() {
// state to contain the category id
const [categoryId, setCategoryId] = useState("");
// get the current pathname
const pathname = usePathname();
// get the category id from the pathname with a server action
// through the useEffect hook
useEffect(() => {
findCategoryBySlug(pathname).then((res) => setCategoryId(res));
}, []);
// return the category id
return <h1>{categoryId}</h1>;
}If think what  @aardani meant is that the function in the "useEffect" can be async and use a Server Action
the useEffect + useState thing is basically how you handle asynchrony in React client component, that do not allow async/await
things like "useTransition" or the incoming "use" hook are similar, they wrap "mean" async code to make it compatible with React client-side rendering approach
@Upland Sandpiper maybe it can be less messy for you now :
js
"use client";
import { usePathname } from "next/navigation";
import { findCategoryBySlug } from "@/lib/actions/category.action";
import { useEffect, useState } from "react";
export default function MyClientComponent() {
// state to contain the category id
const [categoryId, setCategoryId] = useState("");
// get the current pathname
const pathname = usePathname();
// get the category id from the pathname with a server action
// through the useEffect hook
useEffect(() => {
findCategoryBySlug(pathname).then((res) => setCategoryId(res));
}, []);
// return the category id
return <h1>{categoryId}</h1>;
}
yeah this is better. But you dont need to do this. just fetch the data directly in server component and pass it down to your client component
Upland SandpiperOP
Can you show me please
export default async function Page(){
const data = await getData()
return <ClientComponent data={data} />
}Answer