108

I am using mongoose (node), what is the best way to output id instead of _id?

Johnny
  • 2,800
  • 6
  • 25
  • 33
  • 34
    For 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 Answers18

135

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:

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.

Dave Jensen
  • 4,574
  • 1
  • 40
  • 45
Pascal Zajac
  • 2,927
  • 4
  • 14
  • 9
  • 6
    Very 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
  • 6
    This 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 keep coming back to this answer :)) – Developerium May 26 '20 at 19:23
  • I get the following error when I try this soluction `Cannot read properties of undefined (reading 'toHexString')` – insivika Mar 17 '22 at 17:30
57

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
});
xaviert
  • 5,653
  • 6
  • 29
  • 31
55

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.

Benamar
  • 845
  • 8
  • 3
  • 2
    This 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
  • 1
    If 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
  • 1
    Document 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
44

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;
});
evilcelery
  • 15,941
  • 8
  • 42
  • 54
  • 4
    I 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
  • 1
    this 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
    virtuals is probably what you want. – Leonidas Mar 20 '13 at 19:49
34

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;
     }
}); 
Tom Makin
  • 3,203
  • 23
  • 23
26
//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.

joshperry
  • 41,167
  • 16
  • 88
  • 103
user3087827
  • 260
  • 3
  • 4
16

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;
  }
});
Murat Çorlu
  • 8,207
  • 5
  • 53
  • 78
10

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"
}
Abraham
  • 8,525
  • 5
  • 47
  • 53
9

Overwrite default method toJSON by new one:

schema.method('toJSON', function () {
   const { __v, _id, ...object } = this.toObject();
   object.id = _id;
   return object;
});
Stan Fad
  • 1,124
  • 12
  • 23
7

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.

Adam Reis
  • 4,165
  • 1
  • 44
  • 35
5

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) => {
 // 
})
user2062241
  • 111
  • 2
  • 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

d-feverx
  • 1,424
  • 3
  • 16
  • 31
1

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']
  );
Ivor Scott
  • 159
  • 3
  • 12
1

Override toJSONmethod 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
  }
}
Sen Sokha
  • 1,784
  • 16
  • 16
0

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.

Alex Craft
  • 13,598
  • 11
  • 69
  • 133
0

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.

https://mongoosejs.com/docs/guide.html

McNidday
  • 1
  • 1
-2

You can also use pre 'save' hook:

TouSchema.pre('save', function () {      
  if (this.isNew) {
    this._doc.id = this._id;      
  } 
}
-2
JSON.parse(JSON.stringify(doc.toJSON()))
CyberT33N
  • 78
  • 5
  • 1
    Please 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