2 min read
2

Simplify your Next.js Middleware

Next.js Middleware missing polyfill for multiple or complex middlewares

Motivation

I’m working with Next.js project for a few years now, after Vercel moved multiple /**/_middleware.ts files to a single /middleware.ts file, there was a unfilled gap - but just for now. After a 2023 retro I had found that there is no good solution for that problem, so I took matters into my own hands. I wanted to share that motivation with everyone here, as I think that we all need to remember how it all started.

Hope it will save you some time and would make your project DX better!

The problem

By default Next.js supports only one middleware.ts file, in which you need match routes with logic by your own which can be pretty problematic and chaotic. Also it’s endlessly making middleware boilerplate bigger.

/middleware.ts
// example code source: https://github.com/vercel/next.js/blob/canary/examples/middleware/middleware.ts
 
import { NextRequest, NextResponse } from 'next/server'
 
export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname === '/about') {
    return NextResponse.redirect(new URL('/redirected', request.url))
  }
  if (request.nextUrl.pathname === '/another') {
    return NextResponse.rewrite(new URL('/rewrite', request.url))
  }
  return NextResponse.next()
}
 
export const config = {
  matcher: ['/about/:path*', '/another/:path*'],
}

This exmaple is not that scary, but imagine adding more complex logic for few more routes… (ugh)

The solution

Let me introduce to you my latest package called next-easy-middleware.

Installation:

pnpm add next-easy-middleware

Usage:

/middleware.ts
import { createMiddleware } from 'next-easy-middlewares';
import { type NextRequest, NextResponse } from 'next/server';
 
import { analyticsMiddleware } from '@shared/analytics/middleware';
import { authMiddleware, loginMiddleware } from '@/app/(auth)/_middleware';
import { docsMiddleware } from '@/app/(docs)/_middleware';
import { blogMiddleware, blogViewsMiddleware } from '@/app/(blog)/_middleware';
import { postsMiddleware } from '@/app/(posts)/_middleware';
 
const middlewares = {
  '/login': loginMiddleware,
  '/docs/:path*': docsMiddleware,
  '/blog/[slug]': blogMiddleware,
  '/blog/[slug]/view': blogViewsMiddleware,
  'regex:^/posts/\\d+$': postsMiddleware,
};
 
const globalMiddlewares = {
  before: authMiddleware,
  after: analyticsMiddleware
}
 
export const middleware = createMiddleware(middlewares, globalMiddlewares);
 
export const config = {
  matcher: ['/((?!api/|_next/|_static|_vercel|[\\w-]+\\.\\w+).*)'],
};

Feedback

I’m awaiting your feedback about that solution, also feelfree to contribute to that package in below repository.

GiHub Repository

Enjoy!