Skip to content
Back to skills

Create Middleware

ASecurity

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

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
developmenttypescript

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill create-middleware --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Create Middleware?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Create Middleware
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-create-middleware/badge)](https://www.skillsdirectory.com/skills/majiayu000-create-middleware)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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:

```typescript
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:

```typescript
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()`:

```typescript
// 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:

```typescript
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()`:

```typescript
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:

```typescript
// 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](REFERENCE.md) for complete examples:

- `auth.middleware.ts` - Authentication with dependency injection
- `validation.middleware.ts` - Generic validation middleware

## Usage in Routes

```typescript
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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.