Accessing information from Next.js API endpoint in a Next.js web application

Currently, I am in the process of developing a web application using Next.js APP Router for both the frontend and backend components. The frontend takes care of rendering the user interface, while the backend comprises API routes. I require some guidance on how to make fetch requests from the Next.js frontend to the Next.js backend.

The structure of the app directory is as follows:

 ---/(includes all frontend routes in the pages.ts folder)

 ---/api(includes all backend routes in the route.js folder)

Included is a Helper function


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;
}

Using this helper function within the server component functions properly.

For instance:

This particular component utilizes

'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);
}

However, when utilizing the helper function within a server component, it fails to retrieve the default host URL automatically. Therefore, attempting to use fetch separately in the client component like so:

This is a server component example

export default async function Home() {
    const res = await fetch(`/api/products`);
    const products = await res.json();
}

This method of fetching results in an error because the component is server-side and cannot access the host URL by default.

Internal error: TypeError: Failed to parse URL from /api/products

To resolve this issue, I created a workaround by incorporating a .env file containing:

.env files

NEXT_PUBLIC_API_URL=http://localhost:3000

Then, made fetch requests like this:

Server component (with environment fetch)

export default async function Home() {
    const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/products`);
    const products = await res.json();
}

This approach functions flawlessly during development on my local machine and also successfully builds without errors. However, upon attempting to deploy the application on Vercel, an error is thrown despite having checked all backend routes and optimized them for production.

Vercel error message

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 can be understood since the environment variable is set to localhost, causing an expected error. How can one obtain the host URL during build time on Vercel deployment when the application is still being processed?

Is there any alternative way to access the environment variable that corresponds to the URL provided by Vercel upon deployment? Any assistance on this matter would be greatly appreciated.

Answer №1

If you find yourself trying to retrieve your own route handlers from server-side code, such as server components, you're going about it the wrong way. Instead, directly call the server-side logic. To put it in simpler terms:

It's like ordering food from the drive-thru at McDonald's when you're already inside the restaurant.

In coding terms, this could be as simple as copying and pasting the logic into your component.

Before

// 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 ...
}

After

// app/page.tsx
export default async function Page() {
  const data = async db.select(...).from(...)
  return ...
}

The main idea behind server components is that you shouldn't need to fetch your own route handlers; instead, perform the necessary logic like querying directly.

There are several reasons why this approach is preferred and leads to fewer issues:

  • You avoid the hassle of dealing with current API URLs, which can be especially cumbersome in various environments like preview modes. If you find yourself manipulating URLs to point back to your backend, there might be a better solution.
  • Authentication can become complex since client-server requests pass cookies like JWTs, but fetching route handlers from server-side code may not pass on these cookies automatically. This can lead to unauthorized request errors.
  • Static builds will fail if they attempt to fetch from the server itself during build time when it's not running.
  • Favoring fetching over direct calls to underlying logic is less type-safe.
  • Just like the McDonald's analogy, making unnecessary requests while already on the server adds extra time, slowing things down and potentially increasing costs.

In general, route handlers serve two specific purposes:

  • For client-side fetching within your application, when server components are not suitable or feasible.
  • When third parties interact with a publicly available version of your API. In this scenario, consider extracting route handler logic into reusable functions that can be called from both server components and route handlers.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When attempting to integrate Angular 1.4's new router with components, an issue arises where a warning is triggered stating, "Failed to instantiate controller HeadlinesController

Attempting to work with Angular for the first time and struggling to load a component using data from an http request. Currently encountering a warning when attempting to execute the code with the HTTP request. Receiving a "Could not instantiate control ...

The script in NextJs is failing to render properly within the component

I’m currently working on integrating Cookiebot into my NextJs website. The documentation provided mentions that the script for cookie declaration needs to be inserted within the HTML of a specific page, in this case, it's a dedicated subpage solely ...

Exploring the integration of Glide js within a Next js environment

I am trying to figure out how to use a script tag in the next JavaScript _app.js component for utilizing the Glide JavaScript carousel. However, I am facing difficulties in using the script tag. Can someone help me out by providing guidance on how to inc ...

Unable to add or publish text in CKEditor

In my ASP.NET MVC application, I am struggling to post the updated value from a CKEditor in a textarea. Here is the code snippet: <textarea name="Description" id="Description" rows="10" cols="80"> This is my textarea to be replaced with CKEditor ...

Do I require two bot logins for discord.js?

Working on my discord bot, I've been trying to incorporate a script from index.js. Should I also include bot.login at the end of cmdFunctions.js? Here is the content of index.js: const Discord = require('discord.js'); const bot = new Discor ...

JavaScript - Transforming Name:ItemName/Value:ItemValue Pairs into Standard ItemName:ItemValue JSON Format

Looking to reformat JSON data? [{"name":"age","value":31}, {"name":"height (inches)","value":62}, {"name":"location","value":"Boston, MA"}, {"name":"gender","value":"male"}] If you want it to be in a different format: [{"age": 31}, {"height (inches)": 6 ...

Extract information from an array located inside a nested object

My aim is to calculate the length of the skills array for each user individually. To begin, I have this JSON data: const txt = `{ "Alex": { "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" da ...

Is it possible to receive real-time updates for a specific location in Cesium

I am currently developing a live tracking application using Cesium, and I'm encountering an issue with updating the point location on the map in real-time. Currently, my Cesium viewer successfully receives the data from the server in JSON format and ...

Troubleshooting: Issue with AngularJS Image onload directive - "this" reference not functioning properly?

I have a custom directive that looks like this: .directive('ngImageOnLoad', function () { return { restrict: 'A', link: function(scope, element, attrs) { element.bind('load', function() { ...

Choose the div without a class

I currently have several divs displayed on my website. <div class="slide bx-clone"></div> <div class="slide"></div> <div class="slide"></div> <div class="slide bx-clone"></div> <div class="slide bx-clone" ...

Bug in timezone calculation on Internet Explorer 11

I've spent hours researching the issue but haven't been able to find any effective workarounds or solutions. In our Angular 7+ application, we are using a timezone interceptor that is defined as follows: import { HttpInterceptor, HttpRequest, H ...

Are you experiencing issues with your Ajax request?

I've been struggling to retrieve json data from an API. Despite my efforts, the GET request seems to be executing successfully and returning the correct data when I check the Net tab in Firebug. Can anyone offer advice on what could be going wrong or ...

Error encountered: Mocha - The property '$scope' cannot be read as it is undefined

I encountered an issue: Error: Type Error - Cannot read property '$scope' of undefined at $controller (angular/angular.js:10327:28) at angular-mocks/angular-mocks.js:2221:12 at Context. (src/client/app/peer-review/post-visit.co ...

Using RxJS v5 for Sending a POST Request with Parameters

Snippet of my RxJS code: .mergeMap(action => { const user = store.getState().user; return ajax.post(`${config.API_BASE_URL}/api/v1/rsvps`, { rsvp: { meetup_id: action.payload, user_id: user.id, } }) .map(action => calenda ...

Unable to update the numerical value in the Material-UI version 5 TextField component

When attempting to display the decimal value 1.0 in the MUI5's TextField component, I encountered an issue. While I can change its value using the icons within the TextField, inputting any value directly seems to be ineffective. Additionally, backspac ...

It is important to ensure that the user returned by the onAuthStateChanged function in

server admin.auth().createCustomToken(uuid) .then((customToken) => { admin.auth().createUser({ email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ed989e889fad88958c809d8188c38e8280">[email protected] ...

Retrieve the value of a variable by using either an HTTP GET or POST request

Here's the HTML code snippet that I'm working with: <head> <h1>Sample Page</h1> </head> <body> <form method="POST" action=""> Enter Keyword <input type="text" name="key"> ...

Issue with setting a background image to a div in Next.js: not functioning as expected

I haven't used Next.js before and I'm having trouble setting a background image for a specific div. I have a folder named public in the root directory where I've placed the file art-thing.jpg. My styles.css is also in the root directory, whi ...

Using JQuery to retrieve part of a className results in a null value being returned

I am facing an issue with a piece of code in codesandbox where it returns null when I attempt to retrieve part of the className from a div using JQuery. How can I troubleshoot this and make it work? Check out the Codesandbox Example import React, { Compo ...

The SVG format quickly displays new and larger datasets on line charts without any transition effects

My goal is to create a line chart with animated transitions similar to this demo, but without the dots. I am attempting to integrate this functionality into a react component where the update method can be triggered by another component, allowing the d3 op ...