Reputation: 31
I'm working on a web application with both the frontend and backend written using Next.js APP Rouer. The frontend is responsible for rendering the user interface, and the backend consists of API routes. I need assistance with making fetch requests from the Next.js frontend to the Next.js backend.
---/(consists of all the frontend routes folder with page.ts)
---/api(consists of all the backend routes folder route.js)
export const UseFetchFromNext = async (endpoint) =>{
console.log(process.env.NEXT_PUBLIC_VERSION)
const res = await fetch(`${endpoint}`)
const data = await res.json()
console.log(data)
return data;
}
Use of helper function inside server component just work fine
For example
'use client'
useEffect(() => {
fetchData();
console.log(product)
console.log(products)
}, [slug])
const fetchData = async () => {
const productRes = await UseFetchFromNext(`/api/product/${slug}`);
const productsRes = await UseFetchFromNext(`/api/products`);
setProduct(productRes.data);
setProducts(productsRes.data);
}
But when using the helper function on a server component it doesn't grab the default host URL so i tried using fetch separately on client component like this
export default async function Home() {
const res = await fetch(`/api/products`);
const products = await res.json();
}
fetching like this throws an error as the component is server side and cannot get the host url by default
Internal error: TypeError: Failed to parse URL from /api/products
So i made a workaround and create a .env file with
NEXT_PUBLIC_API_URL=http://localhost:3000
and made fetch request like this
export default async function Home() {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/products`);
const products = await res.json();
}
and it is working perfectly fine on developement on my machine
and also the build command builds it without no error
But when i'm trying to host the application on vercel it throws an error as i checks all the backend routes and optimizes it for the production
TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:11576:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
cause: Error: connect ECONNREFUSED 127.0.0.1:3000
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1495:16)
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 3000
}
the Error makes sense the env variable is set to localhost and it is supposed to throw error but how can i get the hostURL in buildtime in vercel when deploying the application as the application is still building.
Is there any work around to get the enviornment variable which is equivalent to the the URL which will be provided by vercel on deployment
Any help will be highly appreciated
Upvotes: 3
Views: 3444
Reputation: 10885
The problem that you are running into is that you are attempting to fetch
your own route handlers from server-side code (in that case server components) which isn't the right way to do that. Instead call the server-side logic directly. Or more informal:
Don't order from the McDonalds drive through when you are already inside the restaurant.
In code this can be almost as trivial as copy-pasting the logic into your component.
// app/api/products/route.ts
export async function GET() {
const data = async db.select(...).from(...)
return Response.json(data)
}
// app/page.tsx
export default async function Page() {
const data = await fetch(...)
return ...
}
// app/page.tsx
export default async function Page() {
const data = async db.select(...).from(...)
return ...
}
The whole point of server components is that you don't need to fetch your own route handlers and can perform the logic such as querying directly.
Reasons why this is idiomatic and causes less problems are the following:
In general route handlers are only designed for two very specific use cases:
Upvotes: 4