Phase 1b: Rename all ~94 commands across 12 plugins to /<noun> <action> sub-command pattern. Git-flow consolidated from 8→5 commands (commit variants absorbed into --push/--merge/--sync flags). Dispatch files, name: frontmatter, and cross-reference updates for all plugins. Phase 2: Design documents for 8 new plugins in docs/designs/. Phase 3: Scaffold 8 new plugins — saas-api-platform, saas-db-migrate, saas-react-platform, saas-test-pilot, data-seed, ops-release-manager, ops-deploy-pipeline, debug-mcp. Each with plugin.json, commands, agents, skills, README, and claude-md-integration. Marketplace grows from 12→20. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.8 KiB
3.8 KiB
name, description
| name | description |
|---|---|
| middleware-catalog | Common middleware patterns for auth, CORS, rate-limiting, logging, and error handling per framework |
Middleware Catalog
Purpose
Reference catalog of middleware patterns for FastAPI and Express. This skill is loaded by the api-architect agent when adding or configuring middleware, ensuring correct implementation per framework.
Middleware Execution Order
Middleware should be registered in this order (outermost to innermost):
- Error Handler - Catches all unhandled exceptions
- CORS - Must run before any route processing
- Request ID - Generate unique ID for tracing
- Logging - Log incoming request details
- Rate Limiting - Reject excessive requests early
- Authentication - Validate credentials
- Authorization - Check permissions (often per-route)
- Validation - Validate request body/params (often per-route)
- Route Handler - Business logic
Authentication Middleware
JWT Bearer (FastAPI)
- Use
fastapi.security.HTTPBearerdependency - Decode token with
python-joseorPyJWT - Store decoded claims in request state
- Return 401 for missing/invalid/expired tokens
- Environment:
JWT_SECRET,JWT_ALGORITHM(default HS256)
JWT Bearer (Express)
- Use
express-jwtor custom middleware - Decode token from
Authorization: Bearer <token>header - Attach decoded user to
req.user - Return 401 for missing/invalid/expired tokens
- Environment:
JWT_SECRET,JWT_ALGORITHM
API Key
- Read from
X-API-Keyheader - Compare against stored keys (database or environment)
- Return 401 for missing key, 403 for invalid key
CORS Middleware
FastAPI
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Restrict in production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
max_age=600,
)
Express
const cors = require('cors');
app.use(cors({
origin: '*', // Restrict in production
credentials: true,
methods: ['GET','POST','PUT','PATCH','DELETE'],
maxAge: 600,
}));
Rate Limiting
Strategies
- Fixed window: N requests per time window (simple, bursty at boundaries)
- Sliding window: Smooth rate enforcement (recommended)
- Token bucket: Allows controlled bursts
Response Headers
X-RateLimit-Limit: Maximum requests allowedX-RateLimit-Remaining: Requests remaining in windowX-RateLimit-Reset: Timestamp when limit resets
Production Notes
- Use Redis as backend store for distributed rate limiting
- In-memory stores do not work with multiple server instances
- Consider different limits for authenticated vs anonymous users
Logging Middleware
Structured Logging Fields
Every request log entry should include:
request_id: Unique identifier for tracingmethod: HTTP methodpath: Request pathstatus_code: Response statusduration_ms: Processing timeclient_ip: Client IP addressuser_id: Authenticated user ID (if available)
Sensitive Field Masking
Never log: Authorization header values, request bodies containing password, token, secret, credit_card fields.
Error Handler
Standard Error Response
All errors must produce consistent JSON:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"request_id": "abc-123"
}
}
Exception Mapping
- Validation errors: 422 with field-level details
- Not found: 404 with resource type and ID
- Authentication: 401 with generic message (no details)
- Authorization: 403 with required permission
- Conflict: 409 with conflicting field
- Internal: 500 with request_id only (no stack traces in production)