middleware tricks
10/6/2024•3 min read
middleware tricks
middleware runs before your request completes. intercepts stuff before it hits the page. perfect for auth, redirects, logging, modifying requests/responses.
not great for heavy lifting though - keep it light. no complex data fetching, heavy compute, or direct db ops. use route handlers for that.
setup
create middleware.ts in your project root:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
export const config = {
matcher: '/about/:path*',
}
matching paths
middleware runs on every route by default. use matcher to target specific ones:
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
regex works too. exclude certain paths:
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
}
NextResponse basics
redirect, rewrite, set headers and cookies:
import { NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
// redirect
return NextResponse.redirect('/new-url')
// or rewrite
// return NextResponse.rewrite('/new-content')
// or modify response
// const response = NextResponse.next()
// response.headers.set('X-Custom-Header', 'sup')
// return response
}
cookies
simple cookie handling:
export function middleware(request) {
// get cookie
let cookie = request.cookies.get('nextjs')
// check if exists
const hasCookie = request.cookies.has('nextjs')
// delete it
request.cookies.delete('nextjs')
// set new cookie
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
return response
}
headers
add custom headers to requests or responses:
export function middleware(request: NextRequest) {
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-custom', 'hello')
const response = NextResponse.next({
request: { headers: requestHeaders },
})
response.headers.set('x-response', 'sup')
return response
}
CORS handling
manage cross-origin requests:
const allowedOrigins = ['https://acme.com', 'https://my-app.org']
const corsOptions = {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
export function middleware(request: NextRequest) {
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)
// handle preflight
if (request.method === 'OPTIONS') {
const preflightHeaders = {
...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
...corsOptions,
}
return NextResponse.json({}, { headers: preflightHeaders })
}
// handle simple requests
const response = NextResponse.next()
if (isAllowedOrigin) {
response.headers.set('Access-Control-Allow-Origin', origin)
}
return response
}
export const config = {
matcher: '/api/:path*',
}
early responses
respond directly without hitting your app:
import { isAuthenticated } from '@lib/auth'
export const config = {
matcher: '/api/:function*',
}
export function middleware(request) {
if (!isAuthenticated(request)) {
return new Response(
JSON.stringify({ success: false, message: 'nope' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
)
}
return NextResponse.next()
}
why use it
- auth & authorization
- session management
- input validation & security
- header manipulation
- server-side redirects based on user role