Nextjs Middleware Creator
Create optimized Next.js middleware with authentication, rate limiting, and routing logic
$ npx claude-code-templates@latest --command="nextjs-vercel/nextjs-middleware-creator" --yesRequires 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: $ARGUMENTSCurrent 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 →
Related Claude Code Commands
Nextjs Component Generator
Generate optimized React components for Next.js with TypeScript and best practices
Nextjs Performance Audit
Comprehensive Next.js performance audit with actionable optimization recommendations
Nextjs Bundle Analyzer
Analyze and optimize Next.js bundle size with detailed recommendations
Vercel Deploy Optimize
Optimize and deploy Next.js application to Vercel with performance monitoring
Nextjs Api Tester
Test and validate Next.js API routes with comprehensive test scenarios
Vercel Env Sync
Synchronize environment variables between local development and Vercel deployments
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.