Claude-skill-registry create-middleware
Create middleware for cross-cutting concerns. Use when creating authentication, validation, or other request processing middleware. Triggers on "create middleware", "auth middleware", "validation middleware".
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/create-middleware" ~/.claude/skills/majiayu000-claude-skill-registry-create-middleware && rm -rf "$T"
skills/data/create-middleware/SKILL.mdCreate Middleware
Creates Hono middleware for cross-cutting concerns like authentication, validation, and error handling.
Quick Reference
Location:
src/middlewares/{middleware-name}.middleware.ts
Naming: Descriptive, kebab-case (e.g., auth.middleware.ts, validation.middleware.ts)
Middleware Types
Examples of middleware types:
| Type | Purpose | Example |
|---|---|---|
| Authentication | Verify user identity, set | |
| Validation | Validate request data, set validated vars | |
| Error Handling | Global error handler for consistent errors | In |
Creating Authentication Middleware
Step 1: Create the File
Create
src/middlewares/auth.middleware.ts
Step 2: Define Dependencies Interface
import { createMiddleware } from "hono/factory"; import type { AppEnv } from "@/schemas/app-env.schema"; import { AuthenticationService } from "@/services/authentication.service"; import { UnauthenticatedError } from "@/errors"; export interface AuthMiddlewareDeps { authenticationService: AuthenticationService; }
Step 3: Create Factory Function
export const createAuthMiddleware = (deps: AuthMiddlewareDeps) => { const { authenticationService } = deps; return createMiddleware<AppEnv>(async (c, next) => { const authHeader = c.req.header("Authorization"); if (!authHeader) throw new UnauthenticatedError("Authorization header is missing."); const parts = authHeader.split(" "); let token: string | undefined; if (parts.length === 2 && parts[0].toLowerCase() === "bearer") { token = parts[1]; } if (!token) throw new UnauthenticatedError("Invalid authorization format."); // Will throw errors if it cannot authenticate const user = await authenticationService.authenticateUserByToken(token); c.set("user", user); await next(); }); };
Step 4: Export Default Instance
const defaultAuthenticationService = new AuthenticationService(); export const authMiddleware = createAuthMiddleware({ authenticationService: defaultAuthenticationService, });
Creating Validation Middleware
The validation middleware is generic and validates body, query, or params using Zod schemas.
Factory Function
import type { MiddlewareHandler } from "hono"; import { createMiddleware } from "hono/factory"; import type { ZodTypeAny } from "zod"; import type { AppEnv } from "@/schemas/app-env.schema"; import { BadRequestError, InternalServerError } from "@/errors"; export type ValidationDataSource = "body" | "query" | "params"; interface ValidationOptions { schema: ZodTypeAny; source: ValidationDataSource; varKey: string; } export const validate = ( options: ValidationOptions, ): MiddlewareHandler<AppEnv> => { const { schema, source, varKey } = options; return createMiddleware<AppEnv>(async (c, next) => { let dataToValidate: unknown; try { switch (source) { case "body": dataToValidate = await c.req.json(); break; case "query": dataToValidate = c.req.query(); break; case "params": dataToValidate = c.req.param(); break; default: throw new InternalServerError(); } } catch (error) { if (error instanceof InternalServerError) throw error; throw new BadRequestError(`Invalid request ${source}.`); } const result = schema.safeParse(dataToValidate); if (!result.success) { const fieldErrors = result.error.flatten().fieldErrors; const fieldErrorMessages = Object.entries(fieldErrors) .map(([field, errors]) => `${field}: ${errors?.join(", ")}`) .join("; "); throw new BadRequestError( `Validation failed for ${source}. ${fieldErrorMessages}`, { cause: result.error.flatten() }, ); } c.set(varKey as keyof AppEnv["Variables"], result.data); await next(); }); };
Patterns & Rules
Use createMiddleware
Factory
createMiddlewareAlways use Hono's
createMiddleware with AppEnv type:
import { createMiddleware } from "hono/factory"; import type { AppEnv } from "@/schemas/app-env.schema"; return createMiddleware<AppEnv>(async (c, next) => { // Middleware logic await next(); });
Dependency Injection Pattern
Create a factory function that accepts dependencies:
export interface MyMiddlewareDeps { someService: SomeService; } export const createMyMiddleware = (deps: MyMiddlewareDeps) => { return createMiddleware<AppEnv>(async (c, next) => { // Use deps.someService await next(); }); }; // Export default instance export const myMiddleware = createMyMiddleware({ someService: new SomeService(), });
Setting Context Variables
Store data for downstream handlers using
c.set():
// In middleware c.set("user", authenticatedUser); c.set("validatedBody", parsedData); // In controller const user = c.var.user as AuthenticatedUserContextType;
Error Throwing
Throw domain errors - global handler converts to HTTP responses:
import { UnauthenticatedError, BadRequestError } from "@/errors"; // Authentication errors if (!token) throw new UnauthenticatedError("Missing token"); // Validation errors if (!result.success) throw new BadRequestError("Invalid data");
Always Call next()
next()After successful processing, always call
await next():
return createMiddleware<AppEnv>(async (c, next) => { // Process request... // Pass to next handler await next(); });
Global Error Handler
The global error handler converts domain errors to HTTP responses:
// In src/errors.ts export const globalErrorHandler = (err: Error, c: Context<AppEnv>) => { console.error(err); if (err instanceof BaseError) { return createErrorResponse(c, err); } else if (err instanceof HTTPException) { return c.json({ error: err.message }, err.status); } else { const internalError = new InternalServerError( "An unexpected error occurred", { cause: err }, ); return createErrorResponse(c, internalError); } }; // Register in app setup app.onError(globalErrorHandler);
Complete Examples
See REFERENCE.md for complete examples:
- Authentication with dependency injectionauth.middleware.ts
- Generic validation middlewarevalidation.middleware.ts
Usage in Routes
import { authMiddleware } from "@/middlewares/auth.middleware"; import { validate } from "@/middlewares/validation.middleware"; import { createNoteSchema, noteQueryParamsSchema } from "@/schemas/note.schema"; import { entityIdParamSchema } from "@/schemas/shared.schema"; // Apply to routes router.get( "/", authMiddleware, validate({ schema: noteQueryParamsSchema, source: "query", varKey: "validatedQuery", }), controller.getAll, ); router.post( "/", authMiddleware, validate({ schema: createNoteSchema, source: "body", varKey: "validatedBody", }), controller.create, ); router.get( "/:id", authMiddleware, validate({ schema: entityIdParamSchema, source: "params", varKey: "validatedParams", }), controller.getById, );
What NOT to Do
- Do NOT use
type directly (useMiddlewareHandler
factory)createMiddleware - Do NOT forget to call
await next() - Do NOT catch errors (let global handler catch them)
- Do NOT access
directly (useprocess.env
)@/env - Do NOT put business logic in middleware (that's for services)
See Also
- Wire middleware to routescreate-routes
- Add custom error typesadd-error-type
- Test middleware handlerstest-middleware