I am using mongoose (node), what is the best way to output id instead of _id?
-
34For new readers: below you will see multiple *different* approaches. It is wise to read them all before choosing, instead of blindly going for accepted or most votes. Cheers! – sshow Feb 08 '18 at 15:17
-
`Brands.find(query).project({ _id: 0, id: '$_id', name: 1 }).toArray()` – Jonatas Walker Apr 11 '22 at 23:42
18 Answers
Given you're using Mongoose, you can use 'virtuals', which are essentially fake fields that Mongoose creates. They're not stored in the DB, they just get populated at run time:
// Duplicate the ID field.
Schema.virtual('id').get(function(){
return this._id.toHexString();
});
// Ensure virtual fields are serialised.
Schema.set('toJSON', {
virtuals: true
});
Any time toJSON is called on the Model you create from this Schema, it will include an 'id' field that matches the _id field Mongo generates. Likewise you can set the behaviour for toObject in the same way.
See:
- http://mongoosejs.com/docs/api.html
- http://mongoosejs.com/docs/guide.html#toJSON
- http://mongoosejs.com/docs/guide.html#toObject
You can abstract this into a BaseSchema all your models then extend/invoke to keep the logic in one place. I wrote the above while creating an Ember/Node/Mongoose app, since Ember really prefers to have an 'id' field to work with.

- 4,574
- 1
- 40
- 45

- 2,927
- 4
- 14
- 9
-
6Very good solution (+1). I would also add `Schema.set('toObject', { virtuals: true })` to be able to see virtuals in output when using `console.log(obj)`. – Tom Mar 26 '14 at 10:55
-
6This keeps the _id field. I prefer the transform solution instead, the one provided by shakinfree worked for me. – Peter Hedberg Apr 12 '14 at 19:01
-
3'toJson' is only when converting to JSON. The more general approach would be to use `toObject`. http://mongoosejs.com/docs/guide.html#toObject – Dave Jensen May 15 '14 at 00:14
-
I was placed it in model, it does not runs, where can I place it? placed under this `const companyCollection = mongoose.Schema({});` using ExpressJs – 151291 Jan 16 '19 at 09:27
-
In the latest version of Mongoose (5.4.20 at this time) there is a virtual `id` getter included/prebuilt in the API. https://mongoosejs.com/docs/guide.html#id – David Ferreira Mar 30 '19 at 02:11
-
-
I get the following error when I try this soluction `Cannot read properties of undefined (reading 'toHexString')` – insivika Mar 17 '22 at 17:30
As of Mongoose v4.0 part of this functionality is supported out of the box. It's no longer required to manually add a virtual id
field as explained by @Pascal Zajac.
Mongoose assigns each of your schemas an id virtual getter by default which returns the documents _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it passing this option at schema construction time. Source
However, to export this field to JSON, it's still required to enable serialization of virtual fields:
Schema.set('toJSON', {
virtuals: true
});

- 5,653
- 6
- 29
- 31
-
5This was true long before v4.0, but what the OP wants is for *just* id (and not _id) to be returned. Just telling mongoose to include the virtual id returns both. – neverfox Jan 03 '16 at 04:54
-
2
I used this :
schema.set('toJSON', {
virtuals: true,
versionKey:false,
transform: function (doc, ret) { delete ret._id }
});
I think it would be great if they automatically suppress _id when virtuals is true.

- 845
- 8
- 3
-
2This one worked for me. Where schema is each model you create. For example: `const UserSchema = new Schema({ stuff }); UserSchema.set("toJSON", { virtuals: true, versionKey: false, transform: function(doc, ret) { delete ret._id; } }); const User = model("user", UserSchema);` – user3152459 Mar 14 '20 at 22:50
-
1If anyone finds this isn't working for them straight away, check that you're not using `lean: true` option when retrieving your documents from the database. – coatesap Jun 17 '20 at 15:05
-
1Document versioning can also be disabled by setting the versionKey to false. DO NOT disable versioning unless you know what you are doing. http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html https://mongoosejs.com/docs/guide.html#versionKey – ET-CS Jul 09 '20 at 22:15
-
what is the 'doc', that we are passing to the function? whats the content of it? – legacy Aug 16 '21 at 07:46
I create a toClient() method on my models where I do this. It's also a good place to rename/remove other attributes you don't want to send to the client:
Schema.method('toClient', function() {
var obj = this.toObject();
//Rename fields
obj.id = obj._id;
delete obj._id;
return obj;
});

- 15,941
- 8
- 42
- 54
-
4I really new to mongoose, this does seem like what I want. how do you call this toClient? Normally I do, find(), and get a docs object back, then just do res.send(docs). So in this case, do I call res.write(docs[1].toClient()) on each doc? thanks – Johnny Aug 12 '11 at 14:20
-
1this doesn't seem to work for me now - has the method of doing this in Mongoose changed? – outside2344 Feb 21 '13 at 17:03
-
6
Here is an alternative version of the answer provided by @user3087827. If you find that schema.options.toJSON
is undefined then you can use:
schema.set('toJSON', {
transform: function (doc, ret, options) {
ret.id = ret._id;
delete ret._id;
delete ret.__v;
}
});

- 3,203
- 23
- 23
//Transform
Schema.options.toJSON.transform = function (doc, ret, options) {
// remove the _id of every document before returning the result
ret.id = ret._id;
delete ret._id;
delete ret.__v;
}
there is a "Schema.options.toObject.transform" property to do the reverse or you could just setup as a virtual id.

- 41,167
- 16
- 88
- 103

- 260
- 3
- 4
If you want to use id
instead of _id
globally then you can set toJSON
config on mongoose object(starting from v5.3):
mongoose.set('toJSON', {
virtuals: true,
transform: (doc, converted) => {
delete converted._id;
}
});

- 8,207
- 5
- 53
- 78
There is also normalize-mongoose
a simple package that removes _id
and __v
for you.
From something like this:
import mongoose from 'mongoose';
import normalize from 'normalize-mongoose';
const personSchema = mongoose.Schema({ name: String });
personSchema.plugin(normalize);
const Person = mongoose.model('Person', personSchema);
const someone = new Person({ name: 'Abraham' });
const result = someone.toJSON();
console.log(result);
So let's say you have something like this:
{
"_id": "5dff03d3218b91425b9d6fab",
"name": "Abraham",
"__v": 0
}
You will get this output:
{
"id": "5dff03d3218b91425b9d6fab",
"name": "Abraham"
}

- 8,525
- 5
- 47
- 53
Overwrite default method toJSON
by new one:
schema.method('toJSON', function () {
const { __v, _id, ...object } = this.toObject();
object.id = _id;
return object;
});

- 1,124
- 12
- 23
I created an easy to use plugin for this purpose that I apply for all my projects and to all schema's globally. It converts _id
to id
and strips the __v
parameter as well.
So it converts:
{
"_id": "400e8324a71d4410b9dc3980b5f8cdea",
"__v": 2,
"name": "Item A"
}
To a simpler and cleaner:
{
"id": "400e8324a71d4410b9dc3980b5f8cdea",
"name": "Item A"
}
Usage as a global plugin:
const mongoose = require('mongoose');
mongoose.plugin(require('meanie-mongoose-to-json'));
Or for a specific schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const MySchema = new Schema({});
MySchema.plugin(require('meanie-mongoose-to-json'));
Hope this helps someone.

- 4,165
- 1
- 44
- 35
You can also use the aggregate function when searching for items to return. $project will allow you to create fields, which you can do and assign it to _id.
<model>.aggregate([{$project: {_id: 0, id: '$_id'}], (err, res) => {
//
})

- 111
- 2
- 2
Create a base schema
import { Schema } from "mongoose";
export class BaseSchema extends Schema {
constructor(sche: any) {
super(sche);
this.set('toJSON', {
virtuals: true,
transform: (doc, converted) => {
delete converted._id;
}
});
}
}
Now in your mongoose model, use BaseSchema
instead of Schema
import mongoose, { Document} from 'mongoose';
import { BaseSchema } from '../../helpers/mongoose';
const UserSchema = new BaseSchema({
name: String,
age: Number,
});
export interface IUser {
name: String,
age: Number,
}
interface IPlanModel extends IUser, Document { }
export const PlanDoc = mongoose.model<IPlanModel>('User', UserSchema);
Typescript implementation of @Pascal Zajac answer

- 1,424
- 3
- 16
- 31
If you are using lodash to pick the elements you want, this will work for you.
UserSchema.virtual('id').get(function(){
return this._id.toHexString();
});
UserSchema.set('toObject', { virtuals: true })
UserSchema.methods.toJSON = function() {
return _.pick(
this.toObject(),
['id','email','firstName','lastName','username']
);

- 159
- 3
- 12
Override toJSON
method for specific model schema.
https://mongoosejs.com/docs/api.html#schema_Schema-method
YourSchema.methods.toJSON = function () {
return {
id: this._id,
some_field: this.some_field,
created_at: this.createdAt
}
}

- 1,784
- 16
- 16
There's another driver that does that http://alexeypetrushin.github.com/mongo-lite set convertId
option to true. See "Defaults & Setting" section for more details.

- 13,598
- 11
- 69
- 133
Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString.

- 1
- 1
You can also use pre 'save' hook:
TouSchema.pre('save', function () {
if (this.isNew) {
this._doc.id = this._id;
}
}

- 1
- 3
JSON.parse(JSON.stringify(doc.toJSON()))

- 78
- 5
-
1Please don't post only code as an answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Tyler2P Dec 04 '21 at 22:46