据 AlphaMoat 统计,截至 2026-09,Nodejs Patterns 累计下载 4,199 次、获 3 个星标,在全部 5,078 个 AI 技能中排名第 2,383 位,在「开发编程」分类(577 个)中排名第 307 位。
| 平台 | 累计安装 | 累计下载 | 星标 | 评论 |
|---|---|---|---|---|
| ClawHub ↗ | 113 | 3,531 | 3 | - |
| SkillHub ↗ | 274 | 4,199 | 3 | — |
各平台的计数口径不同,统计时间不一致。
提供 Node.js 后端模式,涵盖服务搭建、分层架构、中间件、验证、数据库、认证和缓存。
资料发布于 2026-10-27
以下是根据技能文档生成的示例,不代表实际运行结果。
2026-09 起统计方法调整:同名技能按作者分别统计,并覆盖全部技能。图中虚线为调整前的月份,与之后的数据不直接比较,也不计算跨越调整的增长。
| 月份 | 累计下载 | 环比 | 星标 |
|---|---|---|---|
| 2026-09 | 4,199 | — | 3 |
| 2026-08 | 3,485 | +5.6% | 3 |
| 2026-07 | 3,301 | +10.4% | 3 |
| 2026-06 | 2,989 | +7.4% | 3 |
| 2026-05 | 2,782 | — | 3 |
| 2026-03 | 2,105 | — | 3 |
以下是作者发布的原始文件,按纯文本显示,本站未做修改或审核;其中的链接不可点击,命令请核实后再运行。抓取于 2026-10-23。
--- model: standard description: | 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
---
# Node.js Backend Patterns
Patterns for building scalable, maintainable Node.js backend applications with TypeScript.
## 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
用 gh 命令行工具查看 GitHub 上的 PR 检查、工作流运行与 API 查询的技能。
聚合 16 个搜索引擎、无需 API Key 的检索技能。
用命令行直接列出、配置、授权并调用 MCP 服务与工具。
生成有效的 Mermaid 图表,如流程图、时序图、思维导图、ER 图、用户旅程等。
用受管 OAuth 访问 YouTube Data API,检索视频并管理频道内容。
通过受管的 OAuth 连接访问 GitHub REST API。
覆盖规划、实现、验证与测试的编码工作流技能。
整理 Docker 常用命令与容器工作流的技能。
按维度逐项检查代码质量与安全的评审技能。
e2e-testing-patterns 汇集 Playwright 与 Cypress 的关键用户流程测试模式。
UI Design:提供界面布局、字体、色彩、间距、无障碍和组件设计指导。
给代码质量立规矩的技能。
用 Mermaid 文本语法绘制可版本管理的软件图表。
数据月份:2026-09 · 下载/星标/安装数聚合自公开技能注册库(ClawHub、SkillHub),按月更新。口径说明见数据方法。