Next.js allows you to create routing groups to organize your project. I would like to use this information in a middleware to distinguish private groups that need authentication from public routes. E.g. if one requrests a URL that is in routing group (admin)
he would be redirected to a login page if he is not authenticated, while requesting a URL in (marketing)
or (blog)
group does not require authentication.
A lot of examples that I found, like the one from WebDevSimplified (thanks to Kyle), usually make authentication decisions by path names they put in an array.
const privateRoutes = ["/private"]
const adminRoutes = ["/admin"]
async function middlewareAuth(request: NextRequest) {
if (privateRoutes.includes(request.nextUrl.pathname)) {
const user = await getUserFromSession(request.cookies)
if (user == null) {
return NextResponse.redirect(new URL("/sign-in", request.url))
}
}
if (adminRoutes.includes(request.nextUrl.pathname)) {
const user = await getUserFromSession(request.cookies)
if (user == null) {
return NextResponse.redirect(new URL("/sign-in", request.url))
}
if (user.role !== "admin") {
return NextResponse.redirect(new URL("/", request.url))
}
}
}
If I use an array that points to the URL that need authentication, I might forget to add some path later. If I make all URL private by default, I might have to handle a lot of public cases. So the array might get too big. So I thought it would be convenient convention if I could use routing groups instead. Unfortunately I didn't found anything in Next.js 15.3.3.
Does anyone know how to do it? If it is not possible yet, what are your thoughts how to handle it?
I think that issues come from that because (admin) or (marketing) are not present in the final URL path, they don’t exist at runtime and are not accessible to the middleware. Next.js strips them out when generating routes.
please do this
import { NextRequest, NextResponse } from 'next/server'
...
const privateRoutes = ["/private"]
const adminRoutes = ["/admin"]
export async function middlewareAuth(request: NextRequest) {
const path = request.nextUrl.pathname
const requiresAuth = privateRoutes.some(route => path.startsWith(route)) || adminRoutes.some(route => path.startsWith(route))
if (!requiresAuth) return NextResponse.next()
const user = await getUserFromSession(request.cookies)
if (!user) {
return NextResponse.redirect(new URL("/sign-in", request.url))
}
// If it's an admin route, check role
if (adminRoutes.some(route => path.startsWith(route)) && user.role !== "admin") {
return NextResponse.redirect(new URL("/", request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/private/:path*', '/admin/:path*'] // Apply middleware only to these paths
}