
文档教程后端【免费下载链接】nodebestpractices✅ The Node.js best practices list (July 2026)项目地址https://gitcode.com/GitHub_Trending/no/nodebestpractices点击查看免费下载本篇技术指南以 nodebestpractices 仓库中《Lide com erros de forma centralizada. Não dentro de middlewares》一文为核心骨架深入讲解 Node.js 应用中集中式错误处理Centralized Error Handling的设计思想、典型错误流转链路与完整代码实现。读完本文你将掌握如何构建一个唯一的、可复用的错误处理对象把日志记录、监控上报、进程重启决策从中间件中剥离出来并让它同时覆盖 API 请求、定时任务、消息队列消费者与未捕获异常等所有入口的错误场景。为什么必须集中处理错误而不是在中间件里处理分散处理的隐患同一类错误在不同入口被区别对待在缺乏一个专门处理错误的对象时错误被不一致处理的概率会显著增大Web 请求中抛出的错误、应用启动阶段抛出的错误、定时任务Cron/Scheduled Job抛出的错误往往会被写成三套完全不同的处理逻辑。这样一来部分类型的关键错误很容易因为处理不当而从监控雷达下溜走。更关键的是绝大多数 Web 框架如 Express、Koa都提供了错误捕获中间件机制而一个非常典型的错误做法是把全部错误处理逻辑直接写在这个中间件里。一旦这么写你就无法把这套处理逻辑复用到其他场景——中间件只能捕获请求级的错误定时任务、消息队列订阅者、未捕获异常统统覆盖不到。正如原文档所强调的中间件只应负责捕获错误并转发给处理对象真正的处理动作必须交给集中式错误处理器。集中式错误处理器应该承担的三件事一个合格的集中式错误处理对象通常负责让错误可见并决定进程命运记录日志写入格式良好的 logger上报监控把指标/metrics 推送给 Prometheus、CloudWatch、DataDog、Sentry 等监控产品决策是否崩溃判断这是可信可操作错误还是未知错误决定进程是否应当退出重启。在 README.md 的 TL;DR 中该项目同样给出了这一原则的浓缩表述日志、崩溃决策、监控指标等错误处理逻辑应当封装在一个专用的集中式对象中所有入口API、Cron 任务、定时任务在错误发生时都调用它。典型错误流转链路从抛出到集中处理原文给出的标准错误流如下所示某个模块抛出错误 → API 路由器捕获错误 → 将错误传播给负责捕获错误的中间件 → 调用集中式错误处理器 → 处理器判断是否为不可信非可操作错误并据此优雅重启应用分层代码示例数据访问层不处理错误数据访问层DAL的职责是抛出而不是消化错误——在这里处理错误会导致同一错误被多层重复处理// 数据访问层这里不处理错误 DB.addDocument(newCustomer, (error, result) { if (error) throw new Error(对错误做更好的解释以及其它有用参数, other useful parameters) });API 路由层同步与异步错误统一转发路由层同时捕获同步try/catch与异步Promise 的 .catch错误并统一通过next(error)转发给中间件// API 路由代码捕获同步和异步错误并转发给中间件 try { customerService.addNew(req.body).then((result) { res.status(200).json(result); }).catch((error) { next(error) }); } catch (error) { next(error); }错误处理中间件只捕获不处理中间件拿到错误后立即委托给集中式错误处理器并依据返回值决定是否继续沿错误链向下传递交由进程级兜底逻辑处理// 错误处理中间件把处理动作委托给集中式错误处理器 app.use(async (err, req, res, next) { const isOperationalError await errorHandler.handleError(err); if (!isOperationalError) { next(err); } });英文原版 centralizedhandling.md 中给出了等价的 TypeScript 版本并将handleError的第二个参数设计为responseStream——即由错误处理器负责向客户端发送响应// 错误处理中间件TypeScript app.use(async (err: Error, req: Request, res: Response, next: NextFunction) { await errorHandler.handleError(err, res); });进程级兜底覆盖中间件之外的错误入口中间件只能覆盖 Web 请求路径。要真正做到集中还必须在进程层面挂接兜底监听器让未捕获异常与未处理的 Promise 拒绝也汇入同一个处理器process.on(uncaughtException, error { errorHandler.handleError(error); }); process.on(unhandledRejection, (reason) { errorHandler.handleError(reason); });这正呼应了同仓库 catchunhandledpromiserejection.md 的观点只要开发者忘记在某条 Promise 链上写.catch该错误就不会被uncaughtException捕获而直接消失依赖开发者个人纪律来兜底是脆弱的因此强烈建议订阅process.on(unhandledRejection, callback)作为优雅的兜底方案。构建集中式错误处理对象简单版实现将日志、告警、运维队列、可信性判断封装进一个专用对象而不是散落在中间件里module.exports.handler new errorHandler(); function errorHandler() { this.handleError async function(err) { await logger.logError(err); await sendMailToAdminIfCritical; await saveInOpsQueueIfCritical; await determineIfOperationalError; }; }演进版实现结合进程重启决策原文档在何时应退出进程的实践中把handleError与isTrustedError两个能力放在同一个对象中形成完整的决策闭环参见 shuttingtheprocess.md// 集中式错误处理器封装错误处理相关逻辑 function errorHandler() { this.handleError (error) { return logger.logError(error) .then(sendMailToAdminIfCritical) .then(saveInOpsQueueIfCritical) .then(determineIfOperationalError); } this.isTrustedError (error) { return error.isOperational; } } // 假设开发者将已知的可操作错误标记为 error.isOperationaltrue process.on(uncaughtException, (error) { errorManagement.handler.handleError(error); if(!errorManagement.handler.isTrustedError(error)) process.exit(1) });其背后的设计逻辑是可操作错误Operational Error——例如 HTTP 服务因连接问题查询失败——属于你理解发生了什么及其影响的情况通常记录日志即可而程序员错误Programmer Error——例如读取了未定义的值、数据库连接池泄漏——意味着应用可能处于不一致状态此时除了优雅重启没有更好的选择详见 operationalvsprogrammererror.md。Node.js 官方文档也给出了同样的建议throw的运作方式决定了几乎不存在安全原地恢复的路径最稳妥的响应方式就是关闭进程、由 PM2 之类的重启工具以干净状态重新拉起。TypeScript 版本借助内置 Error 与类型收窄仓库在 shuttingtheprocess.md 中给出了带instanceof判断的 TypeScript 实现通过AppError子类与isOperational标记的组合让是否可信的判断从简单的属性读取升级为类型收窄// 集中式错误对象派生自 Node 的内置 Error export class AppError extends Error { public readonly isOperational: boolean; constructor(description: string, isOperational: boolean) { super(description); Object.setPrototypeOf(this, new.target.prototype); // 恢复原型链 this.isOperational isOperational; Error.captureStackTrace(this); } } class ErrorHandler { public async handleError(err: Error): Promisevoid { await logger.logError(err); await sendMailToAdminIfCritical(); await saveInOpsQueueIfCritical(); await determineIfOperationalError(); }; public isTrustedError(error: Error) { if (error instanceof AppError) { return error.isOperational; } return false; // 非 AppError 一律视为不可信错误 } } export const handler new ErrorHandler();反模式把错误处理逻辑直接写在中间件里以下写法虽然能用但它制造了一个致命盲区——谁来处理 Cron 任务和测试中抛出的错误// 中间件直接处理错误——谁来处理 Cron 任务和测试错误 app.use((err, req, res, next) { logger.logError(err); if (err.severity errors.high) { mailer.sendMail(configuration.adminMail, 发生了严重错误, err); } if (!err.isOperational) { next(err); } });这种模式的本质问题是日志、邮件告警、严重级别判断、可信性判断全部耦合进了 Web 请求上下文。当同一个错误发生在非 HTTP 入口定时任务、消息队列订阅者时这段代码根本不会被触发错误处理自然出现缺口。支撑集中式处理的两条配套实践只使用内置 Error 对象并统一扩展为 AppError集中式处理器要可靠地做isOperational判断前提是错误对象本身是规范的。原文档在 useonlythebuiltinerror.md 中强调JavaScript 宽松的特性导致开发者各用各的错误抛出方式——有人抛字符串、有人自定义类型。统一使用 Node.js 内置Error对象可以保持代码与第三方库的一致性并保留 StackTrace 等关键信息。推荐的实践是只扩展一次用一个统一的AppError承载所有应用级错误通过参数区分错误类型而不是为每个错误场景DbError、HttpError各建一个子类// 集中式错误对象派生自 Node 的 Error function AppError(name, httpCode, description, isOperational) { Error.call(this); Error.captureStackTrace(this); this.name name; // ... 此处赋予其它属性 }; AppError.prototype Object.create(Error.prototype); AppError.prototype.constructor AppError; module.exports.AppError AppError; // 客户端抛出异常 if(user null) throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, 进一步解释, true)反面教材则是直接抛字符串throw (How can I add new product when no value provided?)——它完全丢失了堆栈信息与其他关键数据属性也会破坏依赖instanceof Error检查的 API 契约。让异步错误也进入同一条处理链路集中式处理的前提是错误真的能被送到处理器手上。在 asyncerrorhandling.md 中项目建议使用 Async-Await 或 Promise 而非回调风格处理异步错误——回调迫使开发者在每一层手动检查错误、嵌套难以阅读而 Promise 链与try/catch让主代码路径从每函数错误处理中解放出来// 使用 async/await 捕获错误 async function executeAsyncTask () { try { const valueA await functionA(); const valueB await functionB(valueA); const valueC await functionC(valueB); return await functionD(valueC); } catch (err) { logger.error(err); } finally { await alwaysExecuteThisFunction(); } }同时注意 returningpromises.md 中的告诫异步函数返回 Promise 时若不加await错误发生时调用方函数不会出现在堆栈里V8 的 zero-cost async stacktraces 只在 Promise 被await时才扩展 Promise 决议链。因此在返回 Promise 之前务必显式await解析避免堆栈空洞让集中式处理器拿到残缺的定位信息。错误处理架构全景图英文原版文档配有一张错误处理参与者与流转示意图直观展示了模块、路由、中间件与集中式处理器之间的协作关系别忘了测试错误路径集中式处理器上线后同样需要测试来证明它真的兜住了异常。仓库在 testingerrorflows.md 中给出了三种典型测试手段1. 用 Mocha Chai 断言抛出的异常类型describe(Facebook chat, () { it(Notifies on new chat message, () { const chatService new chatService(); chatService.participants getDisconnectedParticipants(); expect(chatService.sendMessage.bind({ message: Hi })).to.throw(ConnectionError); }); });2. 用 sinon 桩掉仓库层并断言 API 返回正确的 HTTP 状态码与日志字段test(When exception is throw during request, Then logger reports the mandatory fields, async () { //Arrange const orderToAdd { userId: 1, productId: 2, }; sinon .stub(OrderRepository.prototype, addOrder) .rejects(new AppError(saving-failed, Order could not be saved, 500)); const loggerDouble sinon.stub(logger, error); //Act const receivedResponse await axiosAPIClient.post(/order, orderToAdd); //Assert expect(receivedResponse.status).toBe(500); expect(loggerDouble.lastCall.firstArg).toMatchObject({ name: saving-failed, status: 500, stack: expect.any(String), message: expect.any(String), }); });3. 直接通过process.emit(uncaughtException)验证进程级兜底链路test(When unhandled exception is throw, Then the logger reports correctly, async () { //Arrange await api.startWebServer(); const loggerDouble sinon.stub(logger, error); const errorToThrow new Error(An error that wont be caught ); //Act process.emit(uncaughtException, errorToThrow); // Assert expect(loggerDouble.calledWith(errorToThrow)); });落地清单把集中式错误处理真正用起来结合本文与仓库中的相关条目一套可落地的接入方案如下统一错误类型只扩展一次内置Error为AppError携带name、httpCode、description、isOperational等上下文属性建立单一处理器创建errorHandler对象集中承载logError、监控指标上报、邮件/运维队列告警与isTrustedError判断中间件只做转发Express/Koa 的错误中间件里只写next(error)或调用errorHandler.handleError(err, res)进程级兜底订阅uncaughtException与unhandledRejection非可信错误调用process.exit(1)交由 PM2 等 Restarter 以干净状态重启异步链路同步化优先使用 async/await Promise 链返回 Promise 前显式await确保集中式处理器拿到的堆栈完整可诊断为错误路径写测试至少覆盖抛错→状态码→日志字段→进程兜底四条链路的断言。相关深入阅读英文原文 centralizedhandling.md、错误分类 operationalvsprogrammererror.md、进程优雅退出 shuttingtheprocess.md、Promise 拒绝兜底 catchunhandledpromiserejection.md、内置 Error 统一化 useonlythebuiltinerror.md。赞分享文档教程后端【免费下载链接】nodebestpractices✅ The Node.js best practices list (July 2026)项目地址https://gitcode.com/GitHub_Trending/no/nodebestpractices点击查看免费下载相关推荐Undici错误处理终极指南构建集中式错误处理中间件的完整教程Undici错误处理终极指南构建集中式错误处理中间件的完整教程 在现代Node.js开发中 Undici错误处理 是确保HTTP客户端稳定性的关键。作为No后端网络通信Cursor Talk To Figma MCP 上手指南如何把 Figma 重复操作交给 AICursor Talk To Figma MCP 上手指南如何把 Figma 重复操作交给 AI 15:00主管要求把 60 张 SKU 卡片统一换成品牌主人工智能AI 应用MCP 服务Koa 错误处理实战指南从中间件 try-catch 到默认错误处理器与 Error 事件Koa 错误处理实战指南从中间件 try catch 到默认错误处理器与 Error 事件 本篇指南以 Koa 官方文档中的错误处理章节为核心结合仓库源码后端Web框架创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考