Agent skill
middleware-patterns
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/middleware-patterns
SKILL.md
Middleware Patterns Skill
Overview
Next.js middleware runs before every request. StepLeague uses it for auth protection and redirects.
Middleware Location
src/
└── middleware.ts ← Runs on every request matching config
Protected Routes Configuration
// src/middleware.ts
export const config = {
matcher: [
// Protected routes - require auth
'/dashboard/:path*',
'/league/:path*',
'/settings/:path*',
'/submit-steps/:path*',
'/claim/:path*',
// Admin routes
'/admin/:path*',
],
};
Pattern 1: Auth Redirect with Full URL Preservation
Problem: Users lose query params after sign-in redirect.
// ❌ WRONG - Loses query params
const redirectUrl = new URL('/sign-in', request.url);
redirectUrl.searchParams.set('redirect', pathname);
// ✅ CORRECT - Preserves full URL
const redirectUrl = new URL('/sign-in', request.url);
const fullOriginalUrl = pathname + (request.nextUrl.search || '');
redirectUrl.searchParams.set('redirect', fullOriginalUrl);
Full Implementation:
import { NextResponse, type NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Check for auth token in cookies
const hasAuthToken = request.cookies.has('sb-access-token') ||
request.cookies.getAll().some(c => c.name.includes('-auth-token'));
if (!hasAuthToken) {
// Preserve full URL including query params
const signInUrl = new URL('/sign-in', request.url);
const fullPath = pathname + (request.nextUrl.search || '');
signInUrl.searchParams.set('redirect', fullPath);
return NextResponse.redirect(signInUrl);
}
return NextResponse.next();
}
Pattern 2: Edge Runtime Compatibility
Critical: Don't use @supabase/supabase-js in middleware - it's not Edge-compatible.
// ❌ WRONG - Breaks Edge runtime
import { createServerSupabaseClient } from '@/lib/supabase/server';
export async function middleware(request: NextRequest) {
const supabase = await createServerSupabaseClient(); // Crashes!
}
// ✅ CORRECT - Check cookies directly
export async function middleware(request: NextRequest) {
const hasAuth = request.cookies.has('sb-access-token');
// Simple check, no Supabase import
}
Pattern 3: Route-Based Logic
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Public routes - skip auth
if (pathname.startsWith('/api/public') ||
pathname === '/sign-in' ||
pathname === '/sign-up') {
return NextResponse.next();
}
// Admin routes - require superadmin
if (pathname.startsWith('/admin')) {
// Note: Full superadmin check happens in API/page
// Middleware just ensures auth exists
if (!hasAuth) {
return NextResponse.redirect(new URL('/sign-in', request.url));
}
}
// Protected routes - require any auth
if (!hasAuth) {
return redirectToSignIn(request);
}
return NextResponse.next();
}
Pattern 4: Header Injection
Add custom headers for downstream use:
export async function middleware(request: NextRequest) {
const response = NextResponse.next();
// Add pathname for layouts
response.headers.set('x-pathname', request.nextUrl.pathname);
// Add timestamp for debugging
response.headers.set('x-middleware-ts', Date.now().toString());
return response;
}
StepLeague Middleware Structure
// src/middleware.ts
import { NextResponse, type NextRequest } from 'next/server';
const PROTECTED_PATHS = [
'/dashboard',
'/league',
'/settings',
'/submit-steps',
'/claim',
'/admin',
];
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Check if route needs protection
const isProtected = PROTECTED_PATHS.some(p => pathname.startsWith(p));
if (!isProtected) {
return NextResponse.next();
}
// Check for auth cookie
const authCookie = request.cookies.getAll()
.find(c => c.name.includes('-auth-token'));
if (!authCookie) {
const signInUrl = new URL('/sign-in', request.url);
const fullPath = pathname + (request.nextUrl.search || '');
signInUrl.searchParams.set('redirect', fullPath);
return NextResponse.redirect(signInUrl);
}
return NextResponse.next();
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
};
Debugging Middleware
Check if middleware runs:
export async function middleware(request: NextRequest) {
console.log('[Middleware]', request.nextUrl.pathname);
// ...
}
Check matcher config:
// Test your matcher at: https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Middleware not running | Matcher excludes path | Update config.matcher |
| Redirect loops | Signin page matched | Add signin to public paths |
| Missing query params | Not preserving search | Use pathname + search |
| Edge runtime error | Using Node.js APIs | Use only Edge-compatible code |
Key File
File: src/middleware.ts
This is the actual middleware implementation. See this file for the current protected paths and auth logic.
Related Skills
auth-patterns- Auth handling after middleware checksupabase-patterns- Database operations (not in middleware)testing-patterns- Auth middleware tests inauth-middleware.test.ts
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?