Claude Skills Guide

Claude Code for Next.js Middleware Workflow Tutorial

Next.js middleware is one of the most powerful features for controlling request flows at the edge. When combined with Claude Code, you can create intelligent, context-aware routing and authentication workflows that adapt to your application’s needs. This tutorial walks you through building practical middleware solutions with Claude Code’s assistance.

Understanding Next.js Middleware Fundamentals

Middleware in Next.js allows you to execute code before a request is completed. It runs on the edge, meaning it’s fast and can intercept requests without hitting your main server. The middleware.ts file (or .js) in your project root is where all middleware logic lives.

When you work with Claude Code, you can describe your middleware requirements in natural language and receive optimized, production-ready code. This dramatically speeds up development cycles and ensures you’re following best practices.

Setting Up Your Middleware File

Create a middleware.ts file in your project root (at the same level as package.json). Here’s a basic structure:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Your middleware logic here
  return NextResponse.next()
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
}

Claude Code can help you customize this matcher pattern based on your specific routing needs. Simply explain what paths you want to match, and Claude generates the appropriate regex pattern.

Building Authentication Middleware

One of the most common use cases for middleware is protecting routes. Here’s how to implement authentication checks:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token')?.value
  const isAuthPage = request.nextUrl.pathname.startsWith('/login')
  const isApiRoute = request.nextUrl.pathname.startsWith('/api')

  // Allow access to login pages
  if (isAuthPage) {
    if (token) {
      return NextResponse.redirect(new URL('/dashboard', request.url))
    }
    return NextResponse.next()
  }

  // Redirect unauthenticated users to login
  if (!token && !isApiRoute) {
    const loginUrl = new URL('/login', request.url)
    loginUrl.searchParams.set('redirect', request.nextUrl.pathname)
    return NextResponse.redirect(loginUrl)
  }

  return NextResponse.next()
}

This middleware checks for an authentication token, redirects authenticated users away from login pages, and protects private routes. Claude Code can extend this pattern to include role-based access control or JWT validation.

Implementing Geo-Based Routing

Next.js middleware excels at geographic routing. You can redirect users based on their location:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const country = request.geo?.country || 'US'
  const pathname = request.nextUrl.pathname

  // Redirect to localized version if available
  const localizedPath = `/${country.toLowerCase()}${pathname}`
  
  // Check if localized version exists (in production, you'd verify this)
  const supportedCountries = ['us', 'eu', 'uk', 'jp']
  const userCountry = country.toLowerCase()

  if (supportedCountries.includes(userCountry) && pathname === '/') {
    return NextResponse.redirect(new URL(localizedPath, request.url))
  }

  return NextResponse.next()
}

This pattern is particularly useful for e-commerce sites, content platforms, and applications that need region-specific content delivery.

Creating A/B Testing Middleware

You can also use middleware to implement A/B testing by assigning users to different variants:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Check if user already has a variant assigned
  let variant = request.cookies.get('ab-variant')?.value

  if (!variant) {
    // Randomly assign variant (50/50 split)
    variant = Math.random() < 0.5 ? 'control' : 'treatment'
  }

  const response = NextResponse.next()
  
  // Set cookie if not present
  if (!request.cookies.get('ab-variant')) {
    response.cookies.set('ab-variant', variant, {
      path: '/',
      maxAge: 60 * 60 * 24 * 30, // 30 days
    })
  }

  // Add variant header for server components
  response.headers.set('x-ab-variant', variant)

  return response
}

In your components, read this header to render the appropriate variant:

// In a Server Component
export default function Page({ headers }) {
  const variant = headers.get('x-ab-variant')
  return (
    <div>
      {variant === 'treatment' ? <NewFeature /> : <OldFeature />}
    </div>
  )
}

Rate Limiting with Middleware

Protect your API routes from abuse with rate limiting:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

// Simple in-memory rate limiting (use Redis for production)
const rateLimitMap = new Map<string, { count: number; resetTime: number }>()

export function middleware(request: NextRequest) {
  if (!request.nextUrl.pathname.startsWith('/api/')) {
    return NextResponse.next()
  }

  const ip = request.ip || request.headers.get('x-forwarded-for') || 'unknown'
  const now = Date.now()
  const limit = 100 // requests
  const windowMs = 60 * 1000 // 1 minute

  const record = rateLimitMap.get(ip)

  if (!record || now > record.resetTime) {
    rateLimitMap.set(ip, { count: 1, resetTime: now + windowMs })
    return NextResponse.next()
  }

  if (record.count >= limit) {
    return NextResponse.json(
      { error: 'Too many requests' },
      { status: 429, headers: { 'Retry-After': '60' } }
    )
  }

  record.count++
  return NextResponse.next()
}

Working with Claude Code for Middleware Development

Claude Code significantly accelerates middleware development. Here are strategies for getting the best results:

  1. Describe your requirements clearly: Instead of asking for “middleware code,” specify exactly what behavior you need—”I need middleware that checks for a JWT token in the Authorization header and validates it against my auth service.”

  2. Iterate on the solution: Start with a basic implementation and ask Claude to add features incrementally. This produces more reliable code than asking for everything at once.

  3. Test edge cases: Ask Claude to identify potential issues with your middleware, such as handling missing cookies, expired tokens, or malformed requests.

  4. Review security implications: Middleware runs before other checks, so security is critical. Have Claude review your implementation for vulnerabilities.

Best Practices for Production Middleware

When deploying middleware to production, follow these guidelines:

Conclusion

Next.js middleware combined with Claude Code creates a powerful workflow for handling authentication, routing, and request processing at the edge. Start with simple implementations and gradually add complexity as your needs grow. Claude Code’s ability to generate, explain, and improve middleware code makes it an invaluable tool for Next.js developers.

Remember to always test your middleware thoroughly in development before deploying to production, as middleware errors can block entire routes from loading.

Built by theluckystrike — More at zovo.one