NestJs+Mongoose中如何返回MongoDB的ObjectId并隐藏元数据
问题
使用NestJS + MongoDB + Mongoose开发CRUD时,执行Get查询遇到两个问题:
_id字段返回空对象{},响应中$__.skipId为true- 响应携带
$__、$isNew等Mongoose内部元数据,仅需返回纯业务数据
尝试过.toJSON()方法,但仅在查询单个对象时生效,查询列表时无效。
服务层代码
import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { CreateInvoiceDto, FilterInvoicesDto, UpdateInvoiceDto, } from '../dtos/invoice.dtos'; import { Invoice } from '../entities/invoice.entity'; @Injectable() export class InvoicesService { constructor( @InjectModel(Invoice.name) private invoiceModel: Model<Invoice>, ) {} async findAll(params?: FilterInvoicesDto) { if (params) { const { limit, offset } = params; return await this.invoiceModel.find().skip(offset).limit(limit).exec(); } return await this.invoiceModel.find().exec(); } async findOne(id: string) { const invoice = await this.invoiceModel.findById(id).exec(); if (!invoice) { throw new NotFoundException(`Invoice #${id} not found`); } return invoice.toJSON(); } }
DatabaseModule代码
import { Module, Global } from '@nestjs/common'; import { ConfigType } from '@nestjs/config'; import { MongooseModule } from '@nestjs/mongoose'; import { MongoClient } from 'mongodb'; import config from '../config'; @Global() @Module({ imports: [ MongooseModule.forRootAsync({ useFactory: (configService: ConfigType<typeof config>) => { const { connection, user, password, host, port, dbName } = configService.mongo; return { uri: `${connection}://${host}:${port}`, user, pass: password, dbName, }; }, inject: [config.KEY], }), ], providers: [ { provide: 'MONGO', useFactory: async (configService: ConfigType<typeof config>) => { const { connection, user, password, host, port, dbName } = configService.mongo; const uri = `${connection}://${user}:${password}@${host}:${port}/?authSource=admin&readPreference=secondary`; const client = new MongoClient(uri); await client.connect(); const database = client.db(dbName); return database; }, inject: [config.KEY], }, ], exports: ['MONGO', MongooseModule], }) export class DatabaseModule {}
查询响应示例
[ { "$__": { "activePaths": { "paths": { "total": "init", "currencyCode": "init", "exhangeRate": "init", "igtf": "init", "islr": "init", "iva_p": "init", "iva_r": "init", "iva": "init", "subtotal": "init", "methodId": "init", "invoiceNumber": "init", "clientId": "init", "_id": "init", "registerDate": "init", "__v": "init" }, "states": { "require": {}, "init": { "_id": true, "clientId": true, "invoiceNumber": true, "methodId": true, "subtotal": true, "iva": true, "iva_r": true, "iva_p": true, "islr": true, "igtf": true, "exhangeRate": true, "currencyCode": true, "total": true, "registerDate": true, "__v": true } } }, "skipId": true }, "$isNew": false, "_doc": { "_id": {}, "clientId": 278, "invoiceNumber": 278, "methodId": "sjbdf,jbs", "subtotal": 11.5, "iva": 11.5, "iva_r": 11.5, "iva_p": 11.5, "islr": 11.5, "igtf": 11.5, "exhangeRate": 25.5, "currencyCode": "USD", "total": 11.5, "registerDate": "2023-02-24T17:00:39.404Z", "__v": 0 } } ]
解决方案
方法1:全局配置Mongoose自动转换纯净JSON
修改DatabaseModule中的Mongoose连接配置,添加全局的JSON转换规则,直接解决元数据和_id问题:
MongooseModule.forRootAsync({ useFactory: (configService: ConfigType<typeof config>) => { const { connection, user, password, host, port, dbName } = configService.mongo; return { uri: `${connection}://${host}:${port}`, user, pass: password, dbName, // 添加全局JSON转换配置 toJSON: { skipId: false, // 强制返回_id字段 virtuals: true, transform: (doc, ret) => { delete ret.__v; // 移除版本号字段 return ret; } }, toObject: { skipId: false, virtuals: true, transform: (doc, ret) => { delete ret.__v; return ret; } } }; }, inject: [config.KEY], }),
配置后所有查询返回的文档都会自动转为纯净JSON,包含_id且无Mongoose元数据。
方法2:手动转换列表查询结果
如果不需要全局配置,可在findAll方法中对列表数据逐个调用.toJSON():
async findAll(params?: FilterInvoicesDto) { let query = this.invoiceModel.find(); if (params) { const { limit, offset } = params; query = query.skip(offset).limit(limit); } const invoices = await query.exec(); // 逐个转换为纯净JSON,指定skipId: false确保_id显示 return invoices.map(invoice => invoice.toJSON({ skipId: false })); }
方法3:单个Schema单独配置转换规则
如果仅需对Invoice实体生效,可在其Schema定义中添加转换规则:
import { Schema } from 'mongoose'; export const InvoiceSchema = new Schema({ clientId: Number, invoiceNumber: Number, methodId: String, subtotal: Number, // ...其他业务字段定义 }, { toJSON: { skipId: false, transform: (doc, ret) => { delete ret.__v; return ret; } }, toObject: { skipId: false, transform: (doc, ret) => { delete ret.__v; return ret; } } });
内容的提问来源于stack exchange,提问作者Andres Linarez
相关产品推荐
相关产品推荐

