Search code examples
urlnext.jsdynamicparametersmiddleware

How can I match dynamic routes in NextJS middleware functions?


I'm trying to make NextJS middleware mark dynamic routea under /dashboard/[user] as protected thereby performing auth checks when navigating to and from such route. However, I have been unable to do that using the convention /dashboard/:path* or /dashboard/(path)

Here is my middleware.ts

import { auth } from "@/auth"
import { NextRequest, NextResponse } from 'next/server'
 
// 1. Specify protected and public routes
const protectedRoutes = ['/dashboard/:param*', '/message/:path*']
const publicRoutes = ['/login', '/signup', '/']
 
export default async function middleware(req: NextRequest) {
  // 2. Check if the current route is protected or public
  const path = req.nextUrl.pathname
  const isProtectedRoute = protectedRoutes.includes(path)
  const isPublicRoute = publicRoutes.includes(path)
 
  // 3. Decrypt the session from the cookie
  const session = await auth();
  console.log('Middleware: Is route protected? ', isProtectedRoute, req.nextUrl.pathname)
 
  // 5. Redirect to /login if the user is not authenticated
  if (isProtectedRoute && session == null) {
    console.log('Should redirect')
    return NextResponse.redirect(new URL('/login', req.nextUrl))
  }
 
  // 6. Redirect to /dashboard if the user is authenticated
  if (
    isPublicRoute &&
    session?.user &&
    !req.nextUrl.pathname.startsWith('/')
  ) {
    return NextResponse.redirect(new URL('/dashboard', req.nextUrl))
  }
 
  return NextResponse.next()
}
 
// Routes Middleware should not run on
export const config = {
  matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
}

The middleware doesn't match dynamic routes

On navigating to user's dashboard at /dashboard/sam32, the middleware doesn't mark it has protected hence fails to performs auth check. Although if I hard-code the user id in the protected routes array, auth checks are performed appropriately. Why does the middleware fail to mark those dynamic routes or is there a different approach to dynamic url identification in NextJS?


Solution

  • As @Dave Lin commented, I was comparing a regex pattern to a string which is why it won't work. I had to modify my code to the following:

    import { auth } from "@/auth"
    import { NextRequest, NextResponse } from 'next/server'
     
    // 1. Specify protected and public routes
    // const protectedRoutes = ['/message/:path*'] commented out
    const publicRoutes = ['/login', '/signup', '/']
     
    export default async function middleware(req: NextRequest) {
      // 2. Check if the current route is protected or public
      const path = req.nextUrl.pathname
      const isProtectedRoute = path.startsWith('/dashboard/')
      const isPublicRoute = publicRoutes.includes(path)
     
      // 3. Decrypt the session from the cookie
      const session = await auth();
     
      // 5. Redirect to /login if the user is not authenticated
      if (isProtectedRoute && session?.user == null) {
        return NextResponse.redirect(new URL('/login', req.nextUrl))
      }
     
      // 6. Redirect to /dashboard if the user is authenticated
      if (
        isPublicRoute &&
        session?.user &&
        !req.nextUrl.pathname.startsWith('/')
      ) {
        return NextResponse.redirect(new URL('/dashboard', req.nextUrl))
      }
     
      return NextResponse.next()
    }
     
    // Routes Middleware should not run on
    export const config = {
      matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
    }