nodejs-patterns

WHAT: Production-ready Node.js backend patterns - Express/Fastify setup, layered architecture, middleware, error handling, validation, database integration, authentication, and caching. WHEN: User is building REST APIs, setting up Node.js servers, implementing authentication, integrating databases, adding validation/caching, or structuring backend applications. KEYWORDS: nodejs, node, express, fastify, typescript, api, rest, middleware, authentication, jwt, validation, zod, postgres, mongodb, redis, caching, rate limiting, error handling

7 stars

Best use case

nodejs-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

WHAT: Production-ready Node.js backend patterns - Express/Fastify setup, layered architecture, middleware, error handling, validation, database integration, authentication, and caching. WHEN: User is building REST APIs, setting up Node.js servers, implementing authentication, integrating databases, adding validation/caching, or structuring backend applications. KEYWORDS: nodejs, node, express, fastify, typescript, api, rest, middleware, authentication, jwt, validation, zod, postgres, mongodb, redis, caching, rate limiting, error handling

Teams using nodejs-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/nodejs-patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/wpank/ai/main/skills/backend/nodejs-patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/nodejs-patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How nodejs-patterns Compares

Feature / Agentnodejs-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

WHAT: Production-ready Node.js backend patterns - Express/Fastify setup, layered architecture, middleware, error handling, validation, database integration, authentication, and caching. WHEN: User is building REST APIs, setting up Node.js servers, implementing authentication, integrating databases, adding validation/caching, or structuring backend applications. KEYWORDS: nodejs, node, express, fastify, typescript, api, rest, middleware, authentication, jwt, validation, zod, postgres, mongodb, redis, caching, rate limiting, error handling

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Node.js Backend Patterns

Patterns for building scalable, maintainable Node.js backend applications with TypeScript.


## Installation

### OpenClaw / Moltbot / Clawbot

```bash
npx clawhub@latest install nodejs-patterns
```


## NEVER

- **NEVER store secrets in code** - Use environment variables, never hardcode credentials
- **NEVER skip input validation** - Validate all input at the middleware layer with Zod/Joi
- **NEVER expose error details in production** - Return generic messages, log details server-side
- **NEVER use `any` type** - TypeScript types prevent runtime errors
- **NEVER skip error handling** - Always wrap async handlers, use global error middleware
- **NEVER use sync operations** - Use async/await for I/O, never `fs.readFileSync` in handlers
- **NEVER trust client input** - Sanitize, validate, and parameterize all queries

## When to Use

- Building REST APIs with Express or Fastify
- Setting up middleware pipelines and error handling
- Implementing authentication and authorization
- Integrating databases with connection pooling and transactions
- Adding validation, caching, and rate limiting

## Project Structure — Layered Architecture

```
src/
├── controllers/     # Handle HTTP requests/responses
├── services/        # Business logic
├── repositories/    # Data access layer
├── models/          # Data models and types
├── middleware/      # Auth, validation, logging, errors
├── routes/          # Route definitions
├── config/          # Database, cache, env configuration
└── utils/           # Helpers, custom errors, response formatting
```

Controllers handle HTTP concerns, services contain business logic, repositories abstract data access. Each layer only calls the layer below it.

## Express Setup

```typescript
import express from "express";
import helmet from "helmet";
import cors from "cors";
import compression from "compression";

const app = express();

app.use(helmet());
app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(",") }));
app.use(compression());
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
```

## Fastify Setup

```typescript
import Fastify from "fastify";
import helmet from "@fastify/helmet";
import cors from "@fastify/cors";

const fastify = Fastify({
  logger: { level: process.env.LOG_LEVEL || "info" },
});

await fastify.register(helmet);
await fastify.register(cors, { origin: true });

// Type-safe routes with built-in schema validation
fastify.post<{ Body: { name: string; email: string } }>(
  "/users",
  {
    schema: {
      body: {
        type: "object",
        required: ["name", "email"],
        properties: {
          name: { type: "string", minLength: 1 },
          email: { type: "string", format: "email" },
        },
      },
    },
  },
  async (request) => {
    const { name, email } = request.body;
    return { id: "123", name };
  },
);
```

## Error Handling

### Custom Error Classes

```typescript
export class AppError extends Error {
  constructor(
    public message: string,
    public statusCode: number = 500,
    public isOperational: boolean = true,
  ) {
    super(message);
    Object.setPrototypeOf(this, AppError.prototype);
    Error.captureStackTrace(this, this.constructor);
  }
}

export class ValidationError extends AppError {
  constructor(message: string, public errors?: any[]) { super(message, 400); }
}
export class NotFoundError extends AppError {
  constructor(message = "Resource not found") { super(message, 404); }
}
export class UnauthorizedError extends AppError {
  constructor(message = "Unauthorized") { super(message, 401); }
}
export class ForbiddenError extends AppError {
  constructor(message = "Forbidden") { super(message, 403); }
}
```

### Global Error Handler

```typescript
import { Request, Response, NextFunction } from "express";
import { AppError, ValidationError } from "../utils/errors";

export const errorHandler = (
  err: Error, req: Request, res: Response, next: NextFunction,
) => {
  if (err instanceof AppError) {
    return res.status(err.statusCode).json({
      status: "error",
      message: err.message,
      ...(err instanceof ValidationError && { errors: err.errors }),
    });
  }

  // Don't leak details in production
  const message = process.env.NODE_ENV === "production"
    ? "Internal server error"
    : err.message;

  res.status(500).json({ status: "error", message });
};

// Wrap async route handlers to forward errors
export const asyncHandler = (
  fn: (req: Request, res: Response, next: NextFunction) => Promise<any>,
) => (req: Request, res: Response, next: NextFunction) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};
```

## Validation Middleware (Zod)

```typescript
import { AnyZodObject, ZodError } from "zod";

export const validate = (schema: AnyZodObject) => {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      await schema.parseAsync({
        body: req.body,
        query: req.query,
        params: req.params,
      });
      next();
    } catch (error) {
      if (error instanceof ZodError) {
        const errors = error.errors.map((e) => ({
          field: e.path.join("."),
          message: e.message,
        }));
        next(new ValidationError("Validation failed", errors));
      } else {
        next(error);
      }
    }
  };
};

// Usage
import { z } from "zod";
const createUserSchema = z.object({
  body: z.object({
    name: z.string().min(1),
    email: z.string().email(),
    password: z.string().min(8),
  }),
});
router.post("/users", validate(createUserSchema), userController.createUser);
```

## Authentication — JWT

### Auth Middleware

```typescript
import jwt from "jsonwebtoken";

interface JWTPayload { userId: string; email: string; }

export const authenticate = async (
  req: Request, res: Response, next: NextFunction,
) => {
  try {
    const token = req.headers.authorization?.replace("Bearer ", "");
    if (!token) throw new UnauthorizedError("No token provided");

    req.user = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;
    next();
  } catch {
    next(new UnauthorizedError("Invalid token"));
  }
};

export const authorize = (...roles: string[]) => {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.user) return next(new UnauthorizedError("Not authenticated"));
    if (!roles.some((r) => req.user?.roles?.includes(r))) {
      return next(new ForbiddenError("Insufficient permissions"));
    }
    next();
  };
};
```

### Auth Service

```typescript
export class AuthService {
  constructor(private userRepository: UserRepository) {}

  async login(email: string, password: string) {
    const user = await this.userRepository.findByEmail(email);
    if (!user || !(await bcrypt.compare(password, user.password))) {
      throw new UnauthorizedError("Invalid credentials");
    }

    return {
      token: jwt.sign(
        { userId: user.id, email: user.email },
        process.env.JWT_SECRET!,
        { expiresIn: "15m" },
      ),
      refreshToken: jwt.sign(
        { userId: user.id },
        process.env.REFRESH_TOKEN_SECRET!,
        { expiresIn: "7d" },
      ),
      user: { id: user.id, name: user.name, email: user.email },
    };
  }
}
```

## Database Patterns

### PostgreSQL Connection Pool

```typescript
import { Pool, PoolConfig } from "pg";

const pool = new Pool({
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT || "5432"),
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  max: 20,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

pool.on("error", (err) => {
  console.error("Unexpected database error", err);
  process.exit(-1);
});

export const closeDatabase = async () => { await pool.end(); };
```

### Transaction Pattern

```typescript
async createOrder(userId: string, items: OrderItem[]) {
  const client = await this.db.connect();
  try {
    await client.query("BEGIN");

    const { rows } = await client.query(
      "INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id",
      [userId, calculateTotal(items)],
    );
    const orderId = rows[0].id;

    for (const item of items) {
      await client.query(
        "INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)",
        [orderId, item.productId, item.quantity, item.price],
      );
      await client.query(
        "UPDATE products SET stock = stock - $1 WHERE id = $2",
        [item.quantity, item.productId],
      );
    }

    await client.query("COMMIT");
    return orderId;
  } catch (error) {
    await client.query("ROLLBACK");
    throw error;
  } finally {
    client.release();
  }
}
```

## Rate Limiting

```typescript
import rateLimit from "express-rate-limit";
import RedisStore from "rate-limit-redis";
import Redis from "ioredis";

const redis = new Redis({ host: process.env.REDIS_HOST });

export const apiLimiter = rateLimit({
  store: new RedisStore({ client: redis, prefix: "rl:" }),
  windowMs: 15 * 60 * 1000,
  max: 100,
  standardHeaders: true,
  legacyHeaders: false,
});

export const authLimiter = rateLimit({
  store: new RedisStore({ client: redis, prefix: "rl:auth:" }),
  windowMs: 15 * 60 * 1000,
  max: 5,
  skipSuccessfulRequests: true,
});
```

## Caching with Redis

```typescript
import Redis from "ioredis";

const redis = new Redis({
  host: process.env.REDIS_HOST,
  retryStrategy: (times) => Math.min(times * 50, 2000),
});

export class CacheService {
  async get<T>(key: string): Promise<T | null> {
    const data = await redis.get(key);
    return data ? JSON.parse(data) : null;
  }

  async set(key: string, value: any, ttl?: number): Promise<void> {
    const serialized = JSON.stringify(value);
    ttl ? await redis.setex(key, ttl, serialized) : await redis.set(key, serialized);
  }

  async delete(key: string): Promise<void> { await redis.del(key); }

  async invalidatePattern(pattern: string): Promise<void> {
    const keys = await redis.keys(pattern);
    if (keys.length) await redis.del(...keys);
  }
}
```

## API Response Helpers

```typescript
export class ApiResponse {
  static success<T>(res: Response, data: T, message?: string, statusCode = 200) {
    return res.status(statusCode).json({ status: "success", message, data });
  }

  static paginated<T>(res: Response, data: T[], page: number, limit: number, total: number) {
    return res.json({
      status: "success",
      data,
      pagination: { page, limit, total, pages: Math.ceil(total / limit) },
    });
  }
}
```

## Best Practices

1. **Use TypeScript** — type safety prevents runtime errors
2. **Validate all input** — Zod or Joi at the middleware layer
3. **Custom error classes** — map to HTTP status codes, use global handler
4. **Never hardcode secrets** — use environment variables
5. **Structured logging** — Pino or Winston with request context
6. **Rate limiting** — Redis-backed for distributed deployments
7. **Connection pooling** — always for databases
8. **Dependency injection** — constructor injection for testability
9. **Graceful shutdown** — close DB pools, drain connections on SIGTERM
10. **Health checks** — `/health` endpoint for liveness/readiness probes

Related Skills

testing-patterns

7
from wpank/ai

Unit, integration, and E2E testing patterns with framework-specific guidance. Use when asked to "write tests", "add test coverage", "testing strategy", "test this function", "create test suite", "fix flaky tests", or "improve test quality".

e2e-testing-patterns

7
from wpank/ai

Build reliable, fast E2E test suites with Playwright and Cypress. Critical user journey coverage, flaky test elimination, CI/CD integration.

websocket-hub-patterns

7
from wpank/ai

Horizontally-scalable WebSocket hub pattern with lazy Redis subscriptions, connection registry, and graceful shutdown. Use when building real-time WebSocket servers that scale across multiple instances. Triggers on WebSocket hub, WebSocket scaling, connection registry, Redis WebSocket, real-time gateway, horizontal scaling.

workflow-patterns

7
from wpank/ai

Systematic task implementation using TDD, phase checkpoints, and structured commits. Ensures quality through red-green-refactor cycles, 80% coverage gates, and verification protocols before proceeding.

estimation-patterns

7
from wpank/ai

Practical estimation techniques for software tasks — methods comparison, decomposition, complexity multipliers, buffer calculation, bias awareness, and communication strategies. Use when estimating features, sprint planning, or presenting timelines to stakeholders.

10x-patterns

7
from wpank/ai

Patterns and practices that dramatically accelerate development velocity. Covers parallel execution, automation, feedback loops, workflow optimization, and anti-pattern avoidance. Use when starting projects, planning sprints, optimizing workflows, or onboarding developers.

react-composition-patterns

7
from wpank/ai

No description provided.

loading-state-patterns

7
from wpank/ai

Patterns for skeleton loaders, shimmer effects, and loading states that match design system aesthetics. Covers skeleton components, shimmer animations, and progressive loading. Use when building polished loading experiences. Triggers on skeleton, loading state, shimmer, placeholder, loading animation.

design-system-patterns

7
from wpank/ai

Foundational design system architecture — token hierarchies, theming infrastructure, token pipelines, and governance. Use when creating design tokens, implementing theme switching, setting up Style Dictionary, or establishing multi-brand theming. Triggers on design tokens, theme provider, Style Dictionary, token pipeline, multi-brand theming, CSS custom properties architecture.

microservices-patterns

7
from wpank/ai

No description provided.

architecture-patterns

7
from wpank/ai

No description provided.

auth-patterns

7
from wpank/ai

Authentication and authorization patterns — JWT, OAuth 2.0, sessions, RBAC/ABAC, password security, MFA, and vulnerability prevention. Use when implementing login flows, protecting routes, managing tokens, or auditing auth security.