Claude Code Plugins

Community-maintained marketplace

Feedback

create-middleware

@madooei/backend-template
1
0

Create middleware for cross-cutting concerns. Use when creating authentication, validation, or other request processing middleware. Triggers on "create middleware", "auth middleware", "validation middleware".

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name create-middleware
description Create middleware for cross-cutting concerns. Use when creating authentication, validation, or other request processing middleware. Triggers on "create middleware", "auth middleware", "validation middleware".

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 injection
  • validation.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 MiddlewareHandler type directly (use createMiddleware factory)
  • Do NOT forget to call await next()
  • Do NOT catch errors (let global handler catch them)
  • Do NOT access process.env directly (use @/env)
  • Do NOT put business logic in middleware (that's for services)

See Also

  • create-routes - Wire middleware to routes
  • add-error-type - Add custom error types
  • test-middleware - Test middleware handlers