Claude Code CommandNextjs Vercel41 installs

Nextjs Middleware Creator

Create optimized Next.js middleware with authentication, rate limiting, and routing logic

Install with the Claude Code Templates CLI
$ npx claude-code-templates@latest --command="nextjs-vercel/nextjs-middleware-creator" --yes

Requires Claude Code. The command adds this command to your project's .claudedirectory — nothing runs on ToolZip's servers.

What's inside this command

Component source (preview)

Next.js Middleware Creator

Middleware Type: $ARGUMENTS

Current Project Analysis

Project Structure

  • Next.js config: @next.config.js
  • Existing middleware: @middleware.ts or @middleware.js (if exists)
  • App directory: @app/ (if App Router)
  • Auth configuration: @auth.config.ts or @lib/auth/ (if exists)

Framework Detection

  • Package.json: @package.json
  • TypeScript config: @tsconfig.json (if exists)
  • Authentication libraries: Detect NextAuth.js, Auth0, or custom auth

Middleware Implementation Strategy

1. Middleware File Structure

Create comprehensive middleware at project root:

middleware.ts                 # Main middleware file
lib/middleware/              # Middleware utilities
├── auth.ts                  # Authentication middleware
├── rateLimit.ts            # Rate limiting logic
├── redirects.ts            # Redirect rules
├── rewrites.ts             # URL rewriting
├── cors.ts                 # CORS handling
├── security.ts             # Security headers
└── types.ts               # TypeScript types

2. Base Middleware Template

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { authMiddleware } from './lib/middleware/auth';
import { rateLimitMiddleware } from './lib/middleware/rateLimit';
import { securityMiddleware } from './lib/middleware/security';
import { redirectMiddleware } from './lib/middleware/redirects';

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  
  // Apply security headers first
  let response = await securityMiddleware(request);
  
  // Apply rate limiting
  const rateLimitResult = await rateLimitMiddleware(request);
  if (rateLimitResult) return rateLimitResult;
  
  // Handle authentication for protected routes
  if (isProtectedRoute(pathname)) {
    const authResult = await authMiddleware(request);
    if (authResult) return authResult;
  }
  
  // Handle redirects
  const redirectResult = await redirectMiddleware(request);
  if (redirectResult) return redirectResult;
  
  // Apply additional headers to response
  if (response) {
    return response;
  }
  
  return NextResponse.next();
}

function isProtectedRoute(pathname: string): boolean {
  const protectedPaths = ['/dashboard', '/admin', '/api/protected'];
  return protectedPaths.some(path => pathname.startsWith(path));
}

export const config = {
  matcher: [
    // Match all request paths except static files and images
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
};

Middleware Components

1. Authentication Middleware

// lib/middleware/auth.ts
import { NextRequest, NextResponse } from 'next/server';
import { jwtVerify } from 'jose';

const JWT_SECRET = new TextEncoder().encode(
  process.env.JWT_SECRET || 'your-secret-key'
);

export async function authMiddleware(request: NextRequest) {
  try {
    // Get token from cookies or Authorization header
    const token = request.cookies.get('auth-token')?.value ||
      request.headers.get('authorization')?.replace('Bearer ', '');

    if (!token) {
      return redirectToLogin(request);
    }

    // Verify JWT token
    const { payload } = await jwtVerify(token, JWT_SECRET);
    
    // Add user info to headers for downstream use
    const response = NextResponse.next();
    response.headers.set('x-user-id', payload.sub as string);
    response.headers.set('x-user-role', payload.role as string);
    
    return response;
    
  } catch (error) {
    console.error('Auth middleware error:', error);
    return redirectToLogin(request);
  }
}

function redirectToLogin(request: NextRequest) {
  const loginUrl = new URL('/login', request.url);
  loginUrl.searchParams.set('callbackUrl', request.url);
  return NextResponse.redirect(loginUrl);
}

// Role-based access control
export function requireRole(allowedRoles: string[]) {
  return async function roleMiddleware(request: NextRequest) {
    const userRole = request.headers.get('x-user-role');
    
    if (!userRole || !allowedRoles.includes(userRole)) {
      return new NextResponse('Forbidden', { status: 403 });
    }
    
    return NextResponse.next();
  };
}

2. Rate Limiting Middleware

// lib/middleware/rateLimit.ts
import { NextRequest, NextResponse } from 'next/server';

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

interface RateLimitConfig {
  windowMs: number; // Time window in milliseconds
  maxRequests: number; // Max requests per window
  keyGenerator?: (request: NextRequest) => string;
}

const defaultConfig: RateLimitConfig = {
  windowMs: 15 * 60 * 1000, // 15 minutes
  maxRequests: 100, // 100 requests per 15 minutes
};

export async function rateLimitMiddleware(
  request: NextRequest,
  config: RateLimitConfig = defaultConfig
) {
  const key = config.keyGenerator 
    ? config.keyGenerator(request)
    : getClientIP(request);
  
  const now = Date.now();
  const clientData = requestCounts.get(key);
  
  // Reset window if expired
  if (!clientData || now > clientData.resetTime) {
    requestCounts.set(key, {
      count: 1,
      resetTime: now + config.windowMs
    });
    return null; // Allow request
  }
  
  // Increment counter
  clientData.count++;
  
  // Check if limit exceeded
  if (clientData.count > config.maxRequests) {
    const resetTime = Math.ceil((clientData.resetTime - now) / 1000);
    
    return new NextResponse('Rate limit exceeded', {
      status: 429,
      headers: {
        'X-RateLimit-Limit': config.maxRequests.toString(),
        'X-RateLimit-Remaining': '0',
        'X-RateLimit-Reset': resetTime.toString(),
        'Retry-After': resetTime.toString(),
      },
    });
  }
  
  return null; // Allow request
}

function getClientIP(request: NextRequest): string {
  return request.headers.get('x-forwarded-for') ||
    request.headers.get('x-real-ip') ||
    request.ip ||
    'unknown';
}

// API-specific rate limiting
export const apiRateLimit = (request: NextRequest) =>
  rateLimitMiddleware(request, {
    windowMs: 60 * 1000, // 1 minute
    maxRequests: 60, // 60 requests per minute
    keyGenerator: (req) => `api:${getClientIP(req)}`,
  });

3. Security Headers Middleware

// lib/middleware/security.ts
import { NextRequest, NextResponse } from 'next/server';

export async function securityMiddleware(request: NextRequest) {
  const response = NextResponse.next();
  
  // Security headers
  const securityHeaders = {
    // XSS Protection
    'X-XSS-Protection': '1; mode=block',
    
    // Content Type Options
    'X-Content-Type-Options': 'nosniff',
    
    // Frame Options
    'X-Frame-Options': 'DENY',
    
    // HSTS
    'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
    
    // Referrer Policy
    'Referrer-Policy': 'strict-origin-when-cross-origin',
    
    // Permissions Policy
    'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
    
    // Content Security Policy
    'Content-Security-Policy': generateCSP(),
  };
  
  // Apply security headers
  Object.entries(securityHeaders).forEach(([key, value]) => {
    response.headers.set(key, value);
  });
  
  return response;
}

function generateCSP(): string {
  const csp = [
    "default-src 'self'",
    "script-src 'self' 'unsafe-eval' 'unsafe-inline'",
    "style-src 'self' 'unsafe-inline'",
    "img-src 'self' data: https:",
    "font-src 'self' data:",
    "connect-src 'self'",
    "frame-ancestors 'none'",
  ];
  
  return csp.join('; ');
}

4. CORS Middleware

// lib/middleware/cors.ts
import { NextRequest, NextResponse } from 'next/server';

interface CorsOptions {
  origin: string | string[] | boolean;
  methods: string[];
  allowedHeaders: string[];
  credentials: boolean;
}

const defaultCorsOptions: CorsOptions = {
  origin: true, // Allow all origins in development
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
  credentials: true,
};

export function corsMiddleware(options: Partial<CorsOptions> = {}) {
  const config = { ...defaultCorsOptions, ...options };
  
  return function cors(request: NextRequest) {
    const response = NextResponse.next();
    const origin = request.headers.get('origin');
    
    // Handle preflight requests
    if (request.method === 'OPTIONS') {
      return handlePreflight(request, config);
    }
    
    // Set CORS headers
    if (shouldAllowOrigin(origin, config.origin)) {
      response.headers.set('Access-Control-Allow-Origin', origin || '*');
    }
    
    if (config.credentials) {
      response.headers.set('Access-Control-Allow-Credentials', 'true');
    }
    
    response.headers.set(
      'Access-Control-Allow-Methods',
      config.methods.join(', ')
    );
    
    response.headers.set(
      'Access-Control-Allow-Headers',
      config.allowedHeaders.join(', ')
    );
    
    return response;
  };
}

function handlePreflight(request: NextRequest, config: CorsOptions) {
  const headers = new Headers();
  const origin = request.headers.get('origin');
  
  if (shouldAllowOrigin(origin, config.origin)) {
    headers.set('Access-Control-Allow-Origin', origin || '*');
  }
  
  if (config.credentials) {
    headers.set('Access-Control-Allow-Credentials', 'true');
  }
  
  headers.set('Access-Control-Allow-Methods', config.methods.join(', '));
  headers.set('Access-Control-Allow-Headers', config.allowedHeaders.join(', '));
  headers.set('Access-Control-Max-Age', '86400'); // 24 hours
  
  return new NextResponse(null, { status: 200, headers });
}

function shouldAllowOrigin(
  origin: string | null,
  allowedOrigin: string | string[] | boolean
): boolean {
  if (allowedOrigin === true) return true;
  if (allowedOrigin === false) return false;
  if (typeof allowedOrigin === 'string') return origin === allowedOrigin;
  if (Array.isArray(allowedOrigin)) return allowedOrigin.includes(origin || '');
  return false;
}

5. Redirect and Rewrite Middleware

```typescript

// lib/middleware/redirects.ts

import { NextRequest, NextResponse } from 'next/server';

interface RedirectRule {

source: string | RegExp;

destination: string;

permanent?: boolean;

conditions?: (request: NextRequest) => boolean;

}

const redirectRules: RedirectRule[] = [

// Legacy URL redirects

{

source: '/old-page',

destination: '/new-page',

permanent: true,

},

// Dynamic redirects

{

source: /^\/user\/(.+)$/,

destination: '/profile/$1',

permanent: false,

},

// Conditional redirects

{

source: '/admin',

destination: '/admin/dashboard',

conditions: (request) => {

const userRole = request.headers.get('x-user-role');

return userRole === 'admin';

},

},

// Maintenance mode

{

source: /.*/,

destination: '/maintenance',

conditions: (request) => {

return process.env.MAINTENANCE_MODE === 'true' &&

!request.nextUrl.pathname.startsWith('/maintenance');

},

},

];

export async function redirectMiddleware(request: NextRequest) {

const { pathname } = request.nextUrl;

for (const rule of redirectRules) {

if (shouldApplyRule(rule, pathname, request)) {

const destination = resolveDestination(rule.destination, pathname);

const url = new URL(destination, request.url);

return NextResponse.redirect(url, {

status: rule.permanent ? 301 : 302,

});

}

}

return null; // No redirect needed

}

function shouldApplyRule(

rule: RedirectRule,

pathname: string,

request: NextRequest

): boolean {

// Check pattern match

const matches = typeof rule.source === 'string'

? pathname === rule.source

: rule.source.test(pathname);

if (!matches) return false;

// Check additional conditions

if (rule.conditions) {

return rule.conditions

Preview truncated. View the full source on GitHub →

Type
Command
Category
Nextjs Vercel
Installs
41
Source
GitHub ↗

Related Claude Code Commands

Catalog data and component content are sourced from the open-source davila7/claude-code-templates project (MIT license). ToolZip curates the listing and writes original descriptions; every component links back to its original source. Claude Code is a product of Anthropic. ToolZip is an independent catalog and is not affiliated with or endorsed by Anthropic.