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

Ways to verify if all files have been loaded using Firebase.util pagination

Is there a way to confirm if it is necessary to stop invoking the loadMore() function, since all documents have been retrieved from the database? In the code snippet provided, Ionic framework is used as an example, but the same concept applies to ng-infin ...

My handleChange function is inaccessible to the event listener

ParentComponent.js (App.js) import React from "react"; import ChildComponent from "./ChildComponent"; import data from "./data"; import "./styles.css"; class ParentComponent extends React.Component { constructor() ...

Tips for decreasing the width of a Grid component in React using MUI

Is there a way to adjust the width of the initial Grid element within Material UI, allowing the remaining 3 elements to evenly occupy the extra space? see visual example Would modifying the grid item's 'xl', 'lg', 'md', ...

executing npm tasks concurrently

Trying to demonstrate running npm tasks in parallel is my current task. I should be able to achieve this using "&" for parallel and "&&" for series. { "name": "npm", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "co ...

Incorporating Middleware-Managed User Permissions into Menu Items within Next.js Version 14

Working on a Next.js 14 project, I am implementing permissions such as User.Read and Role.Read using middleware to control user access. Although the setup is working well, I am struggling with integrating these permissions from middleware into my menu item ...

Functionality of jQuery Mobile Page

$(document).on("pageshow", "#mappage", function (event) { $("#doldur").append("<li> <label onclick='getBina(" + featuressokak[f].attributes.SOKAKID + ");'>" ...

Error message occurred stating "error running npm start due to spawn C:WINDOWSSystem32WindowsPowerShellv1.0powershell ENOENT"

Whenever I attempt to run npm start, this is the issue that arises. It seems like there might be a problem with PowerShell rather than npm because npm successfully starts the development server. By the way, I created a basic React app using npx create-reac ...

The message appearing on my screen reads: "Attempting to read properties of an undefined value, specifically 'map'."

I am attempting to create a map of my various people, but I keep encountering an error with the title. I'm having trouble understanding where the issue lies, here is my code snippet: export const Testt = ({ childs}) => { console.log(childs) ...

Exploring through angular.js with the use of identifiers

In my data, I have two JSON objects: all_users "all_users": { "4":{ "user_id":4, "user_name":"Miranda" }, "7":{ "user_id":7, "user_name":"seconduser" } And tickets "tickets": [{ "ticket_id" : 12, "created_by" : ...

Loading images in advance with AJAX for enhanced AJAX performance

My website is structured in a sequential manner, where page1.html leads to page2.html and so on. I am looking to preload some images from the third page onto the second page. After searching, I came across this amazing code snippet: $.ajax({ url ...

What is the best way to retrieve the value of a nested function in JavaScript?

I am currently working on a project that involves a function. function findParentID(parentName) { Category.findOne({ categoryName: parentName }, function (err, foundParent) { var parentID = foundParent.categoryID;    return parentID;<br> } ...

Copying to the clipboard now includes the parent of the targeted element

Edit - More Information: Here is a simplified version of the sandbox: https://codesandbox.io/s/stupefied-leftpad-k6eek Check out the demo here: The issue does not seem to occur in Firefox, but it does in Chrome and other browsers I have a div that disp ...

Having trouble getting the expected transition effects to work with Vue.js

Currently, I have a display of lists of items through the use of v-for. Initially, only the summary section of each item is visible. Upon clicking on an item, the details section is supposed to appear. This functionality is achieved by adding or removing ...

What are the steps for scheduling asynchronous tasks using Promises in Node.js?

As a beginner in Javascript, I am struggling to understand how to execute my functions sequentially. My goal is to use Promises to accomplish this task. I am currently following a tutorial on creating a chat bot for Facebook Messenger. Essentially, I want ...

Run a Javascript function two seconds after initiating

My goal is to implement a delay in JavaScript using either setInterval or setTimeout, but I am facing an issue where the primary element is getting removed. Code that works fine without Javascript delay: const selectAllWithAttributeAdStatus = document. ...

Rotating an object around another object in a 3D matrix axis using Three.js

I currently have the ability to rotate one axis. Here is the code that's working now: https://codepen.io/itzkinnu/full/erwKzY Is there a way to rotate an object on a random axis instead of just one fixed axis? Maybe something similar to this exam ...

Error encountered in VUE JS 3: The function vue.initDirectivesForSSR is not recognized as a valid function

I've been encountering some errors while trying to build my Vue web app with this configuration. I have searched for a solution, but couldn't find anyone facing the same issue as me. Any suggestions on how to resolve this problem? The build was s ...

Using Mongoose to increment a sub-document key

I could really use some assistance with the following issue. I'm trying to increment the keys like 'eagle', 'bear', etc. by one in my data model: { "_id" : ObjectId("57e134097a578b11eruogf0a2cb"), "email" : "<a href="/cdn-cgi/l ...

The for...of loop cannot be used with the .for property since it is not iterable. (Is

let arr = [{ category: 'music', views: 20 }, { category: 'abc', views: 32 }, { category: 'bob', views: 20 } ] for (const [key, value] of arr) { console.log(key, value) } console.log(Array ...

What is the reason behind being able to assign unidentified properties to a literal object in TypeScript?

type ExpectedType = Array<{ name: number, gender?: string }> function go1(p: ExpectedType) { } function f() { const a = [{name: 1, age: 2}] go1(a) // no error shown go1([{name: 1, age: 2}]) // error displayed ...