如何在Express中正确实现MongoDB错误处理?最佳实践探讨
问题描述
我拥有一个简易的用户仓库,创建用户时使用try-catch块捕获唯一约束错误并抛出BadRequestError,后续由全局处理器返回对应响应;未知错误则通过process.exit(1)使应用崩溃。但每个数据库查询都添加try-catch并检查错误码既重复又易遗漏;若省略try-catch在中间件处理,又会暴露MongoDB底层错误。现咨询三个问题:
- 每次执行数据库查询都需使用try-catch吗?
- 是否应在此处检测具体错误类型(如重复字段错误),仓库中该如何处理已知与未知错误?
- 遇到未知错误时是否应崩溃应用?例如将
ascending设为2会触发错误,崩溃应用是否合适,还是返回500状态码并保持运行?
相关代码如下:
@Service() export class UserRepository { async getAllSorted(ascending: boolean): Promise<UserDto[]> { let sort = {}; sort = { createdAt: ascending ? 1 : -1 }; const users = await User.find({}).sort(sort); return users.map(toUserDto); }; async create(user: UserCreateDto): Promise<UserDto> { const newUser = new User(user); try { const createdUser = await newUser.save(); return toUserDto(createdUser); } catch (error) { if (error.code === 11000) { // duplicate field error console.error(error); throw new BadRequestError('Unique constraint failed while creating the user.', error.keyValue); } else { throw error; } } } }
我深知当前应用简单,但希望学习最佳实践,感谢解答。
解答
1. 每次执行数据库查询都需使用try-catch吗?
不需要每个查询都单独写try-catch,这种做法会导致代码冗余且容易遗漏。更高效的方式是在业务层或全局中间件统一捕获异步错误,比如在NestJS中使用全局异常过滤器,或者在控制器层用try-catch包裹业务方法的调用。
不过仓库层可以针对特定操作的已知错误(比如创建用户时的唯一约束冲突)做针对性捕获转换,其他通用查询的错误交给上层统一处理,避免重复代码。
2. 是否应在此处检测具体错误类型,仓库中该如何处理已知与未知错误?
- 已知错误(如唯一约束冲突
11000):应该在仓库层检测并转换为业务相关的自定义错误(比如BadRequestError)。仓库层最清楚数据库操作可能触发的特定错误,在这里转换能让上层业务逻辑不用关心数据库底层的错误码,只处理带有业务语义的错误。 - 未知错误:仓库层不需要处理,直接抛出即可。上层的全局异常过滤器会统一捕获这些错误,转换为通用的500响应,同时过滤掉MongoDB的底层细节(比如错误栈、数据库结构信息),避免暴露敏感内容。
可以把数据库错误的转换逻辑封装成工具函数,减少重复代码:
function transformDbError(error: any): never { if (error.code === 11000) { throw new BadRequestError('Unique constraint failed', error.keyValue); } // 可扩展添加其他已知数据库错误的转换逻辑 throw error; } // 在create方法中复用 async create(user: UserCreateDto): Promise<UserDto> { const newUser = new User(user); try { const createdUser = await newUser.save(); return toUserDto(createdUser); } catch (error) { transformDbError(error); } }
3. 遇到未知错误时是否应崩溃应用?
不建议直接用process.exit(1)让应用崩溃,除非是不可恢复的致命错误(比如数据库连接完全断开且无法重连)。
像你提到的把ascending设为2触发的错误,属于参数验证不严谨导致的数据库操作错误,这类错误应该:
- 先在控制器或DTO层做参数校验(比如用class-validator限制
ascending只能是boolean类型),从源头避免错误; - 如果还是触发了未知错误,全局异常过滤器捕获后返回500 Internal Server Error响应,同时记录详细的错误日志(供排查),保持应用继续运行。
应用崩溃会导致所有正在处理的请求失败,严重影响用户体验,而大多数未知错误是临时或局部的,保持应用运行可以继续处理其他请求。只有当错误会导致数据不一致或应用完全无法提供服务时,才考虑终止应用。
内容的提问来源于stack exchange,提问作者Fatih Yıldız
相关产品推荐
相关产品推荐

