create-middleware
About
This Claude skill generates Hono middleware for cross-cutting concerns like authentication, validation, and error handling. It creates properly structured middleware files in the correct project location when triggered by phrases like "create middleware" or "auth middleware." The output follows naming conventions and includes examples for common middleware types.
Quick Install
Claude Code
Recommended/plugin add https://github.com/majiayu000/claude-skill-registrygit clone https://github.com/majiayu000/claude-skill-registry.git ~/.claude/skills/create-middlewareCopy and paste this command in Claude Code to install this skill
Documentation
Create 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 c.var.user | auth.middleware.ts |
| Validation | Validate request data, set validated vars | validation.middleware.ts |
| Error Handling | Global error handler for consistent errors | In src/errors.ts |
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
Always 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()
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:
auth.middleware.ts- Authentication with dependency injectionvalidation.middleware.ts- Generic validation middleware
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
MiddlewareHandlertype directly (usecreateMiddlewarefactory) - Do NOT forget to call
await next() - Do NOT catch errors (let global handler catch them)
- Do NOT access
process.envdirectly (use@/env) - Do NOT put business logic in middleware (that's for services)
See Also
create-routes- Wire middleware to routesadd-error-type- Add custom error typestest-middleware- Test middleware handlers
GitHub Repository
Related Skills
algorithmic-art
MetaThis Claude Skill creates original algorithmic art using p5.js with seeded randomness and interactive parameters. It generates .md files for algorithmic philosophies, plus .html and .js files for interactive generative art implementations. Use it when developers need to create flow fields, particle systems, or other computational art while avoiding copyright issues.
subagent-driven-development
DevelopmentThis skill executes implementation plans by dispatching a fresh subagent for each independent task, with code review between tasks. It enables fast iteration while maintaining quality gates through this review process. Use it when working on mostly independent tasks within the same session to ensure continuous progress with built-in quality checks.
executing-plans
DesignUse the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.
cost-optimization
OtherThis Claude Skill helps developers optimize cloud costs through resource rightsizing, tagging strategies, and spending analysis. It provides a framework for reducing cloud expenses and implementing cost governance across AWS, Azure, and GCP. Use it when you need to analyze infrastructure costs, right-size resources, or meet budget constraints.
