據 AlphaMoat 統計,截至 2026-09,Nodejs Patterns 累計下載 4,199 次、獲 3 個星標,在全部 5,078 個 AI 技能中排名第 2,383 位,在「開發程式設計」分類(577 個)中排名第 307 位。
| 平台 | 累計安裝 | 累計下載 | 星標 | 評論 |
|---|---|---|---|---|
| ClawHub ↗ | 113 | 3531 | 3 | - |
| SkillHub ↗ | 274 | 4199 | 3 | — |
各平台的計數口徑不同,統計時間不一致。
提供 Node.js 後端模式,涵蓋服務搭建、分層架構、中介軟體、驗證、資料庫、認證和快取。
資料釋出於 2026-10-27
以下是根據技能文件生成的示例,不代表實際執行結果。
2026-09 起統計方法調整:同名技能按作者分別統計,並覆蓋全部技能。圖中虛線為調整前的月份,與之後的資料不直接比較,也不計算跨越調整的增長。
| 月份 | 累計下載 | 環比 | 星標 |
|---|---|---|---|
| 2026-09 | 4199 | — | 3 |
| 2026-08 | 3485 | +5.6% | 3 |
| 2026-07 | 3301 | +10.4% | 3 |
| 2026-06 | 2989 | +7.4% | 3 |
| 2026-05 | 2782 | — | 3 |
| 2026-03 | 2105 | — | 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),按月更新。口徑說明見資料方法。