Reputation: 3651
This is in a NextJS
version 14.2.7 app.
I have setup up an api under the following path.
src/app/api/posts/route.ts
When I call it from a client component as follows, works fine.
const loadMorePosts = async () => {
const res = await fetch(`/api/posts?page=1&limit=20`);
const data = await res.json();
};
useEffect(() => {
if (inView) {
loadMorePosts();
}
}, [inView]);
But if I call the same endpoint from a server component as follows:
import { notFound } from 'next/navigation';
const getPost = async (slug: string) => {
const res = await fetch(`/api/posts?page=1&limit=20`); // this already breaks
const data = await res.json();
return data;
};
const PostDetails = async ({ slug }: { slug: string }) => {
const post = await getPost(slug);
// failing before even coming here.
if (!post) {
notFound();
}
return (
<div className='container mx-auto p-4'>
Hello
</div>
);
};
export default PostDetails;
I get this error:
GET /api/posts?page=1&limit=20 200 in 1069ms ✓ Compiled /post/[slug] in 153ms (1069 modules) ⨯ Internal error: TypeError: Failed to parse URL from /api/posts?page=1&limit=20 at node:internal/deps/undici/undici:12500:13 digest: "1300981860" Cause: TypeError: Invalid URL at new URL (node:internal/url:804:36) at new Request (node:internal/deps/undici/undici:4839:25) at fetch (node:internal/deps/undici/undici:9651:25) at fetch (node:internal/deps/undici/undici:12498:10) at fetch (node:internal/bootstrap/web/exposed-window-or-worker:79:16) at doOriginalFetch (webpack-internal:///(rsc)/./node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected]/node_modules/next/dist/server/lib/patch-fetch.js:440:24) at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected]/node_modules/next/dist/server/lib/patch-fetch.js:589:24) at async getPost (webpack-internal:///(rsc)/./src/components/PostDetails.tsx:12:17) at async PostDetails (webpack-internal:///(rsc)/./src/components/PostDetails.tsx:39:18) {
code: 'ERR_INVALID_URL', input: '/api/posts?page=1&limit=20'
Why? I have also tried with localhost:3000/api/posts?page=1&limit=20. Same error.
Upvotes: 2
Views: 69
Reputation: 98
If you need your api call only once in the server component, your solution is good. But, if you need the same data both on client and server component, there are 2 better solutions (in my opinion)
const res = await fetch(`${process.env.ORIGIN}/api/posts?page=1&limit=20`);
Hope this will help
Upvotes: 0
Reputation: 3651
Reason is cos when it comes to server components, you no longer need to call that logic as an api. Just call it as a function without need for going via fetch
.
Essentially extract the logic. Say example you need to contact DB and fetch some data.
export const getPost = async (slug: string) => {
const post = await db
.select()
.from(posts)
.where(eq(posts.slug, slug))
.limit(1)
.then(rows => rows[0] ?? null);
return post;
};
Now in server component, call this function to fetch data.
No need to go via http(s) nor fetch.
const PostDetails = async ({ slug }: { slug: string }) => {
const post = await getPost(slug);
if (!post) {
notFound();
}
return (
<div className='container mx-auto p-4'>
Hello
</div>
);
};
Hope this helps others.
Upvotes: 0