Creating a fetcher that seamlessly functions on both the server and client within Nextjs 13 - the ultimate guide!

My Nextjs 13 frontend (app router) interacts with a Laravel-powered backend through an api. To handle authentication in the api, I am utilizing Laravel Sanctum as suggested by Laravel for SPAs. This involves setting two cookies (a session and a CSRF token) after login, which must be included in all future requests along with a X-Xsrf-Token header that carries the CSRF token.

To make a request from a client component, I can use the following code:

import cookies from 'js-cookie'

fetch( [url], {
   headers: {
      "Accept": "application/json",
      "Content-Type": "application/json",
      "Origin": [origin],
      "X-Requested-With": "XMLHttpRequest",  
      "X-Xsrf-Token": cookies.get( "XSRF-TOKEN" ),
      // Cookies are automatically attached via the 'Cookie' header on browser
   },
   credentials: 'include'
})

If I need to send a request from a server component, I can do:

import { cookies, headers } from "next/headers"

fetch( [url], {
   headers: {
      "Cookie": cookies().toString(), 
      "Referer": headers().get( "referer" ) ?? "",
      "X-Xsrf-Token": cookies().get( "XSRF-TOKEN" ).value,
   },
})

While both methods work, sometimes I need to send the same request from both server and client components. In such cases, I want to create a single fetcher like this:

import cookies from 'js-cookie'
import { cookies, headers } from "next/headers"

fetch( [url], 
   isServer 
   ?
   {
      headers: {
         "Cookie": cookies().toString(),
         "Referer": headers().get( "referer" ) ?? "",
         "X-Xsrf-Token": cookies().get( "XSRF-TOKEN" ).value,
      }   
   }
   :
   {
      headers: {
         "Accept": "application/json",
         "Content-Type": "application/json",
         "Origin": process.env.NEXT_PUBLIC_APP_URL,
         "X-Requested-With": "XMLHttpRequest",  
         "X-Xsrf-Token": cookies.get( "XSRF-TOKEN" ),
      },
      credentials: 'include'
   } 
)

However, this setup causes errors in client components due to restrictions on using cookies() and headers() functions outside of server components. So, my question is how can I create a universal fetcher that functions seamlessly on both server and client components?

Answer №1

Yes, the cookies() and headers() functions are meant for server-side use only and cannot be used in client components. These functions are specifically designed to be utilized in server components or server actions.

However, it is possible to create a utility function that can determine if the code is executing on the server or the client, and then execute the fetch accordingly. Below is a simple example:

import cookies from 'js-cookie'
import { cookies as serverCookies, headers } from "next/headers"

function isServer() {
  return typeof window === 'undefined';
}

async function universalFetch(url, options = {}) {
  if (isServer()) {
    options.headers = {
      ...options.headers,
      "Cookie": serverCookies().toString(),
      "Referer": headers().get("referer") ?? "",
      "X-Xsrf-Token": serverCookies().get("XSRF-TOKEN").value,
    };
  } else {
    options.headers = {
      ...options.headers,
      "Accept": "application/json",
      "Content-Type": "application/json",
      "Origin": process.env.NEXT_PUBLIC_APP_URL,
      "X-Requested-With": "XMLHttpRequest",  
      "X-Xsrf-Token": cookies.get("XSRF-TOKEN"),
    };
    options.credentials = 'include';
  }

  return fetch(url, options);
}

In this universalFetch function, it first determines whether it is being executed on the server or the client using the isServer function. If running on the server, it utilizes the serverCookies and headers functions from next/headers to set the necessary headers. On the other hand, when running on the client side, it uses the cookies function from js-cookie to retrieve the XSRF-TOKEN and includes the credentials option set to 'include'.

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

Is it possible to use nodemailer locally with NodeJS? The issue is that the greeting emails are not being received

Every time I attempt to send an email using nodemailer within my local network, I encounter the following error: *Greeting never received at SMTPConnection._formatError (C:\Users\PI_TEAM\Desktop\node_modules\nodemailer\lib ...

Retrieve JSON data from an HTTP request using Node.JS

Hi there, I'm struggling with the Node.js HTTPS request. Basically, I send a request to a server and it responds with a JSON message that I need to parse and save in a variable so I can use it in other functions. let obj=JSON.parse(response); return ...

Iteratively sift through data for boolean value

My navigation dynamically retrieves routes from the vue-router, eliminating the need for manual addition. Within these routes, there is a boolean key named "inMenu" in both parent and child routes. I have successfully filtered out the parent routes based ...

The POST request functions smoothly in Postman, however, encounters an error when executed in node.js

Just recently I began learning about node.js and attempted to send a post request to an external server, specifically Oracle Commmerce Cloud, in order to export some data. Check out this screenshot of the request body from Postman: View Request Body In Pos ...

CKEditor directive in AngularJS does not properly enforce the maxlength attribute in textarea

I am currently working on an AngularJS application with the CKEditor plugin. I have created a directive for CKEditor and everything seems to be functioning properly. However, I am facing an issue where I need to limit the character length to 50. I tried us ...

Transferring a variable value between functions using autocomplete and AJAX communication

I am facing difficulties with implementing autocomplete jQuery along with AJAX call. The issue arises when a user enters text in the input field, triggering an AJAX POST request to the controller which retrieves values from the database and sends them back ...

I'm having trouble getting my innerHTML command to update anything on the webpage, and the reason is eluding me

Below is the code snippet provided: <div id="js"><button onclick="document.getElementById('js').innerHTML=('<form> <input type=text name=tick1></input> <input type=text name=tick2></input> ...

Prisma is now live - The user with the name 'xxx' has reached the limit for 'max_user_connections' in production

Encountering an issue when deploying my Next.js app in production (on Vercel). The app runs smoothly in development with only 1 connection being created. However, in production, it quickly hits the maximum user connections limit (5). I'm unsure of wh ...

Is there a way to incorporate personalized image placeholders into Next.js?

The Image component has properties called placeholder and blurDataURL. The placeholder property can have a value of either 'blur' or 'empty', with no other option. I tried setting the placeholder to 'blur' and specifying the b ...

Automatically populate form fields with data from the clicked row in the HTML table when using JSP

Attempting to populate form fields using jQuery or JavaScript with row elements selected by clicking on the row. Tried a solution from Stack Overflow that didn't work as expected. I'm new to this, so please bear with me. (http://jsbin.com/rotuni/ ...

How can VueJS cycle through the arrays within an object?

Is there a way to efficiently iterate through arrays within an object like in this example? I have a simple code snippet that currently outputs indexes in order, but I'm looking to access the values of the array instead. <template> <div&g ...

What is the mechanism for invoking functions defined with the arrow syntax in Angular?

Referencing this code snippet from the tutorial at https://angular.io/tutorial/toh-pt4, specifically within the hero.component.ts file: getHeroes(): void { this.heroService.getHeroes() .subscribe(heroes => this.heroes = heroes); } After analyz ...

Socket.io-powered notification system

I'm currently in the process of developing a notification system for my Events Manager Website. Every time a user is logged in and performs an action (such as creating an event), a notification about the event creation should be sent to other user ...

Reopen a Kendo UI dialog

Currently, I am utilizing Kendo UI, and my goal is to display a modal dialog when a button is clicked. The issue I am facing is that it works perfectly the first time around. However, upon closing the dialog and attempting to reopen it by clicking the butt ...

The Next.js 13 internationalization website continues to redirect to the locale page despite my attempts to remove it

While working on my app with NextJs, I attempted to implement localisation, but it ended up causing confusion and issues. The application started lagging on the i18n route and became unresponsive, even after multiple attempts of restarting the development ...

Is it possible to utilize AJAX to load the URL and then extract and analyze the data rather than

I had originally created a web scraping method using PHP, but then discovered that the platform I was developing on (iOS via phone gap) did not support PHP. Fortunately, I was able to find a solution using JS. $(document).ready(function(){ var container ...

Finding a solution for the network error encountered during the execution of XMLHttpRequest.send in the specified file path (...distfxcoreservermain.js:200

Having recently delved into Angular, I successfully completed the development of my angular web application. While using ng serve for production, everything ran smoothly. However, after incorporating angular universal and attempting npm run dev:ssr or np ...

Node.js cannot access the uploaded image data as it has been defined as undefined

I have encountered an issue while sending an image file through ajax to my Node.js server. Upon attempting to view the file data, it returns 'undefined'. Here is a snippet from my app.js file: var express = require("express"); var app ...

Express js is not returning a value from a recursive function?

I've been working on an ecommerce website where I created a mongoose model for all categories. However, the challenge arises when dealing with subcategories that require a parent id in the database. When a request is made, all categories are retrieved ...

The JavaScript function on the specified /url page is not functioning properly following the execution of history.push(/url) on click

I have a JavaScript function that toggles the display of login password. However, when I redirect to the login page from another page using history.push(/login), the function does not work. It does work when I use (/login) in the href tag. How can I resolv ...