You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJs+Mongoose中如何返回MongoDB的ObjectId并隐藏元数据

问题

使用NestJS + MongoDB + Mongoose开发CRUD时,执行Get查询遇到两个问题:

  1. _id字段返回空对象{},响应中$__.skipId为true
  2. 响应携带$__、$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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 16:47:03