Extending NextRequest to include a new "auth" property: A step-by-step guide

Currently, I am utilizing the App Router in nextjs14 with stacked middleware to handle localization and authentication. The issue arises when trying to access auth in the Request object while using next-auth v5 for authentication.

Interestingly, the code functions perfectly in middleware.ts without any additional middleware for localization. However, as soon as other middlewares are added, request.auth becomes undefined and triggers a TypeScript error stating "Property 'auth' does not exist on type 'NextRequest'.ts(2339)".

The working code snippet in middleware.ts looks like this:

import NextAuth from "next-auth";

import authConfig from "@/auth.config";

import {
  DEFAULT_LOGIN_REDIRECT,
  apiAuthPrefix,
  authRoutes,
  publicRoutes
} from "@/routes";

const { auth } = NextAuth(authConfig);

export default auth((req) => {
    // Middleware logic here
      
      const { nextUrl } = req;
      const isLoggedIn = !!req.auth;

      console.log("req.auth: ", req.auth);

      const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
      const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
      const isAuthRoute = authRoutes.includes(nextUrl.pathname);
      
      console.log({
        url: nextUrl.pathname,
        isApiAuthRoute: isApiAuthRoute,
        isAuthRoute: isAuthRoute,
        isPublicRoute: isPublicRoute,
        isLoggedIn: isLoggedIn,
      });

      if (isApiAuthRoute) {
        return null;
      }

      if (isAuthRoute) {
        if (isLoggedIn) {
          return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl));
        }
        return null;
      }


      if (!isLoggedIn && !isPublicRoute && !isAuthRoute) {
        return Response.redirect(new URL("/en/login", nextUrl));
        // return null;
      }

      return null;
})

export const config = {
    matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};

However, the following code in middlewares/authMiddleware.ts encounters issues:

import {
  NextFetchEvent,
  NextRequest,
  NextResponse
} from "next/server";
import { MiddlewareFactory } from "@/lib/definitions";

import NextAuth from "next-auth";

import authConfig from "@/auth.config";

import {
  DEFAULT_LOGIN_REDIRECT,
  apiAuthPrefix,
  authRoutes,
  publicRoutes
} from "@/routes";

const { auth } = NextAuth(authConfig);

function getSearchParam(param: string, url: any) {
  return url.searchParams.get(param);
}

export const authMiddleware: MiddlewareFactory = (next) => {
  return async(request: NextRequest, _next: NextFetchEvent) => {
    const pathname = request.nextUrl.pathname;

    if (["/"]?.some((path) => pathname.startsWith(path))) {
      // Middleware logic here
      
      const { nextUrl } = request;
      const isLoggedIn = !!request.auth;

      console.log("request.auth: ", request);

      const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
      const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
      const isAuthRoute = authRoutes.includes(nextUrl.pathname);
      
      console.log({
        url: nextUrl.pathname,
        isApiAuthRoute: isApiAuthRoute,
        isAuthRoute: isAuthRoute,
        isPublicRoute: isPublicRoute,
        isLoggedIn: isLoggedIn,
      });

      if (isApiAuthRoute) {
        return null;
      }

      if (isAuthRoute) {
        if (isLoggedIn) {
          return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl));
        }
        return null;
      }


      if (!isLoggedIn && !isPublicRoute && !isAuthRoute) {
        return Response.redirect(new URL("/en/login", nextUrl));
        // return null;
      }

      return null;

    // End
    }
    return next(request, _next);
  };
};

An attempt was made to add the following code:

import { NextRequest as OriginalNextRequest } from "next/server";

declare global {
    declare interface NextRequest extends OriginalNextRequest {
        auth: any
    }
}

This modification was made in global.d.ts at the root of my project, using it instead of calling NextRequest from next/server. Unfortunately, an error occurred:

Type '(next: NextMiddleware) => (request: NextRequest, _next: NextFetchEvent) => Promise<NextMiddlewareResult>' is not assignable to type 'MiddlewareFactory'.
  Type '(request: NextRequest, _next: NextFetchEvent) => Promise<NextMiddlewareResult>' is not assignable to type 'NextMiddleware'.
    Types of parameters 'request' and 'request' are incompatible.
      Property 'auth' is missing in type 'import("p:/cts-final/frontend/node_modules/next/dist/server/web/spec-extension/request").NextRequest' but required in type 'NextRequest'.ts(2322)
global.d.ts(5, 9): 'auth' is declared here.

I am seeking guidance on how to extend NextRequest to include the auth property, assuming that is the appropriate solution?

Answer №1

To define custom attributes for the NextRequest interface, create a new file with a .d.ts extension (for example: types.d.ts) and add your desired attributes as shown below:

import { NextRequest as DefaultNextRequest } from 'next/server'


declare global {
  declare interface NextRequest extends DefaultNextRequest {
    // add more attributes here
    auth: {
      user: User
    }
  }
}

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

What is the best way to prevent TSC from including the node_modules folder in my compilation

I have encountered a persistent issue that all previous solutions have failed to address. Here is the snippet from my tsconfig file that I believe should resolve the problem: ... "compilerOptions": { "skipLibCheck": true, ... &quo ...

Guide on how to prevent click events when a checkbox is not selected in Angular 2

A click event is being used on a ul element with a checkbox below it. When the checkbox is checked, the list should be enabled and the click event should work. If the checkbox is unchecked, the list should be disabled and the click event should not work. ...

Having trouble with HTTP requests not functioning properly in Nextron (Next.js + Electron) during the production build phase

I have been searching all over the internet trying to figure out what I'm missing, but I can't seem to find a solution for my issue. After creating an electron app with 'nextron' on top of my existing nextjs app, everything was running ...

I'm interested in learning how to implement dynamic routes in Nexy.js using TypeScript. How can I

I have a folder structure set up like this: https://i.stack.imgur.com/qhnaP.png [postId].ts import { useRouter } from 'next/router' const Post = () => { const router = useRouter() const { pid } = router.query return <p>Post: {p ...

Using ts-jest for mocking internal modules

Within my .ts module, I have the following code: import client from './client'; export default class DefaultRequest implements IRequest { make(req: Request): Promise<Response> { return new Promise<Response>((resolve, reje ...

Issue: The variable 'HelloWorld' has been declared but not utilized

Why am I encountering an error when using TypeScript, Composition API, and Pug templating together in Vue 3? How do I resolve this issue when importing a component with the Composition API and using it in a Pug template? ...

The useDisclosure() function is not available in the Chakra UI library

An unanticipated runtime error occurred Error: (0 , chakra_ui_react__WEBPACK_IMPORTED_MODULE_2_.useDisclosure) does not seem to be recognized as a function I have verified that I am importing useDisclosure correctly from chakraUI, however I still receive ...

Tips for correctly displaying diacritics with Webpack and Typescript

While working on my project, I encountered an issue with diacritics marks (such as German or Polish characters) when using Webpack with Typescript. Unfortunately, the problem arises when trying to display these marks in the console or on a webpage. It seem ...

What is the best way to use Shadcn to incorporate a calendar that takes up half of my website?

Currently, I am in the process of developing a scheduling appointment system. My main challenge is getting the calendar to take up half of my page's space. Despite my attempts to adjust its height and width, I have not been successful in seeing any ch ...

I am facing an issue with TypeScript as it is preventing me from passing the prop in React and Zustand

interface ArticuloCompra { id: string; cantidad: number; titulo: string; precio: number; descuento: number; descripcion: string; imagen: string; } const enviarComprasUsuarios = ({ grupos, }: { grupos: { [key: string]: ArticuloCompra & ...

Warning: The `className` property in Nextjs Tailwind did not find a match

I keep receiving this warning message and I'm not sure how to resolve it: Warning: Prop `className` did not match. Server: "flex items-center justify-start my-2 p-4 text-sm w-full hover:text-white rounded-r-lg \n font-medium text ...

Selecting an object dynamically to add a new property in JavaScript

I am faced with a scenario where I have two objects and I need to add new properties to them. The challenge is that I want to be able to choose which object to work with first before adding the new properties. Here's the proposed logic : let customiz ...

Lazy Loading Child Components using Angular 2+ Router

I've been attempting to implement lazy loading on a children route that is already lazy loaded, but I haven't had any success so far. Here is the route structure I am working with: const routes: Routes = [ { path: 'customers', ...

The ngOnChanges lifecycle hook is triggered only once upon initial rendering

While working with @Input() data coming from the parent component, I am utilizing ngOnChanges to detect any changes. However, it seems that the method only triggers once. Even though the current value is updated, the previous value remains undefined. Below ...

Tips for running Typescript files on a server in the background

I am currently working on a NodeJS application that consists solely of TypeScript files, without any JavaScript files. I'd like to run it on my server in the background. Any suggestions on how I can achieve this? I experimented with using the npm pa ...

Deployment failure due to undetected development keys in gitignore

I have a TypeScript-coded Express server with three files for keys in the compiled and pre-compiled code: /// dev.ts - development keys const keys = { googleClientSecret: "GOOGLE_KEY", mongoURI: "mongodb+srv://MONGO_K ...

Developing various VueJS TypeScript projects utilizing a shared library

In the process of developing two VueJS applications using TypeScript, I have created one for public use and another as an admin tool exclusively for my own use. Both applications are being built and tested using vue-cli with a simple npm run serve command. ...

Cypress encounters a SyntaxError while running in continuous integration due to an unexpected token 'export' with the cypress-io/github-action@v2 typescript

Within my cypress plugin file located at frontend/cypress/plugins/index.ts, I have the following code snippet: export default ((on, config) => { // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config }) ...

How can I avoid a component from triggering its subscription twice in Angular 5 when the component is nested twice inside another component?

How can I prevent a component in Angular 5 from triggering its subscription twice when the component is placed twice inside another component? For example, I have a NavMenuComponent with two instances of a cart in its template. <!-- cart 1 in n ...

I'm having trouble getting getStaticProps to work properly. Can anyone lend a hand?

import React from 'react' import styles from './page.module.css' export async function getStaticProps (){ const data = await fetch('https://jsonplaceholder.typicode.com/todos') const todos = await data.json(); c ...