NextJS
Middleware in NextJS

What is Middleware in Next.js?

Middleware in Next.js is a function that allows you to run code before a request is completed. It sits between the incoming request and the response, enabling you to execute logic before rendering a page, fetching data, or returning the response. Middleware can be used for tasks such as authentication, logging, redirecting, and rewriting URLs.

Middleware functions are defined in a middleware.js file placed at the root of the project and are executed for every request, making them useful for global request handling.


Key Concepts of Middleware

  • Runs before rendering: Middleware is triggered before the requested page is rendered or the API route is executed.
  • Edge-based: Middleware runs at the edge, meaning it operates close to the user for faster execution.
  • Conditional Execution: You can specify certain conditions (e.g., routes or specific headers) under which the middleware should run.
  • Response Manipulation: Middleware can manipulate requests (like modifying headers or cookies) and redirect or rewrite requests to different routes.

Typical Use Cases for Middleware

  • Authentication/Authorization: Redirect users if they are not authenticated.
  • Custom redirects/rewrites: Dynamically rewrite URLs or redirect requests based on specific conditions.
  • Analytics/Logging: Capture and log information about requests.
  • Geo-based content delivery: Show specific content based on the user’s location.

Example of Middleware in Next.js

Let's go through a basic example of how to use middleware in a Next.js app for handling authentication:

  1. Creating a middleware.js file: Middleware is written in the root of your Next.js project in a middleware.js file.

    import { NextResponse } from 'next/server';
     
    export function middleware(req) {
      // Retrieve the URL and cookies from the incoming request
      const url = req.nextUrl;
      const token = req.cookies.get('auth-token');
     
      // Check if the user is trying to access a protected route
      if (url.pathname.startsWith('/dashboard')) {
        // If no auth-token cookie is present, redirect to the login page
        if (!token) {
          return NextResponse.redirect('/login');
        }
      }
     
      // Allow the request to proceed if the token exists
      return NextResponse.next();
    }

    🔄 Flow:

    1. The middleware intercepts every request.
    2. It checks whether the user is trying to access a route that starts with /dashboard.
    3. If the user is not authenticated (i.e., no auth-token cookie), it redirects them to the /login page.
    4. If the user is authenticated, it allows the request to proceed with NextResponse.next().

Explanation of Key Functions

  • NextResponse: This is a helper function in Next.js that allows you to create responses, such as redirections or rewrites.
    • NextResponse.next(): Continues with the normal request flow.
    • NextResponse.redirect('/path'): Redirects the request to a new URL.
  • req.cookies.get('cookie-name'): Fetches the value of a specific cookie from the incoming request.

More Complex Middleware Example: URL Rewriting

Middleware can also be used to rewrite URLs dynamically based on user conditions, like location or device type:

import { NextResponse } from 'next/server';
 
export function middleware(req) {
  const country = req.headers.get('x-country'); // Assume a custom header for location
 
  // If user is from 'US', rewrite their request to the US-specific version of the page
  if (country === 'US') {
    return NextResponse.rewrite(new URL('/us-homepage', req.url));
  }
 
  // For other countries, proceed as normal
  return NextResponse.next();
}

🔄 Flow:

  1. The middleware checks the x-country header, which contains the user's country.
  2. If the user is from the US, the request is rewritten to serve a US-specific homepage (/us-homepage).
  3. If the user is from any other country, the request proceeds as normal.

Using Middleware Selectively

You can apply middleware to specific routes by using a matcher. Here's how to ensure the middleware runs only on /dashboard routes:

export const config = {
  matcher: ['/dashboard/:path*'], // Middleware will run for any /dashboard/* route
};

This limits the middleware to run only when the user accesses routes that match /dashboard and its subpaths.


Middleware Flow in a Request Cycle

Client Request → Middleware → Next.js Rendering (getServerSideProps or getStaticProps) → Response to Client

In this flow:

  • The client makes a request.
  • Middleware intercepts the request and can modify it (e.g., adding headers, redirecting).
  • After middleware executes, the request continues to the regular server-side logic (getServerSideProps, etc.).
  • Finally, the server sends a response back to the client.

Limitations of Middleware

  • Edge Functions Only: Middleware runs on the edge, meaning it can't access node.js-specific modules like fs or process.
  • Limited to Request Handling: Middleware cannot modify the actual response body—only the headers, redirects, or rewrites.

Conclusion

Middleware in Next.js is a powerful tool for pre-processing incoming requests, enabling tasks like authentication, URL rewriting, and analytics before the server-side rendering process. It runs at the edge, making it fast and efficient, but is limited to handling requests and headers, without direct access to response bodies or filesystem operations.