danger in using id property for Mongoose models? - node.js

Database design decision making here and there's no other way to do this but poll the experts on SO.
We are using the Mongoose ODM with Node.js Express. MongoDB uses _id as the default property on models which hold ObjectIDs unique to each document.
We are using an auto-incrementer which gives integer ids to each model. We want to call this field 'id' which will complement the '_id' field.
My concern is that there is a small risk that using an 'id' field will cause problems. Does anyone have experience positive or negative with using 'id' for integer ids on all their Mongoose models?

yes, be very careful. Mongoose by default adds a virtual property called "id" to your model. This virtual property returns the cast to String of the _id property.
If you really want to add a real property "id" to your model your must turn off this automatic virtual using the option: {id: false} in your new Schema() calls:
new Schema({name: String, id: int}, {id: false})
Be aware that even if this possible you will confuse every developer who knows mongoose when he reads your code by doing this.
Maybe better call your property differently.
Or when you create this incrementing id anyway as your primary key maybe you can assign it to _id directly because then there is probably no need to generate an additional ObjectId anyway... and the id property will then automatically return your integer as a String.

Related

How to expose MongoDB documents primary keys in a REST API?

I am building a REST API with MongoDB + nodeJS. All the documents are stored and are using _id as the primary key. I've read here that we should not expose the _id and we should use another ID which is not incremental.
In the DB, a document is represented as:
{
_id: ObjectId("5d2399b83e9148db977859ea")
bookName: "My book"
}
For the following the endpoints, how should the documents be exposed?
GET /books
GET /books/{bookId}
Currently my API returns:
{
_id: "5d2399b83e9148db977859ea"
bookName: "My book"
}
but should it instead return something like:
{
id: "some-unique-id-generated-on-creation"
bookName: "My book"
}
Questions
Should I expose the _id so that one can make queries such as:
GET /books/5d2399b83e9148db977859ea
Should I use a UUID for my ID instead of ObjectId?
Should I keep the internal _id (but never expose it) and create another attribute id which would use UUID or another custom generated ID ?
Is it a good practice to work with _id in my backend or should I only make queries using my own custom ID? Example: find({ id: }) instead of find({ _id: })
To answer your questions.
You can expose _id so that authenticated users can make queries like GET, PUT and PATCH on that _id.
MongoDB has support that allows you to generate your own BSON ID and use it, instead of mongodb created it's own _id during the insert.
There is no need of duplicating logic, the main purpose of _id is to identify each document separately and having two id columns means you are storing redundant data, follow DRY (DO NOT REPEAT YOURSELF) principle wherever possible.
It's not a bad practice to work with _id in your backend.
Hope this helps!
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.
You can refer the following docs:
1) https://mongoosejs.com/docs/api.html
2) toObject method
In my case, whether it's a security risk or not, but my _id is a concatenation of any of the fields in my Document that are semantically considered as keys, i.e. if i have First Name, Last Name, and Email as my identifier, and a fourth field such as Age as attribute, then _id would be concatenation of all these 3 fields. It would not be difficult to get and update such record as long as I have First Name, Last Name and email information available

Mongoose - create model from existing db data

Using Mongoose, I have a document that was previously pulled from the database, complete with an _id property, in raw Object format (IE, without all of the document methods, just straight from the db).
How can I use that data to create an instance of mongoose.Model without the system assigning the model a new _id? I want to then eventually save that model and have it update the existing document in the database.
Update: using a combination of #Jack Newcombe's method, and subsequently setting model.isNew to false, I get the following error: "Mod on _id not allowed". So now it knows to update, but Mongoose is not removing the _id field from the update request. There has to be one more system property on the Model that tells Mongoose whether or not to remove the _id during an update request. Any ideas?
I've figured it out, but I'm sure there is a better way. I'm surprised there isn't a way to do this easily with Mongoose's API.
Anyway, you need to the following:
Create the model like this: var model = new Model(data,
{_id:false});
Manually set model.isNew to false
Manually tell Mongoose that the _id field hasn't been modified, like this: delete model.$__.activePaths.states.modify._id
The reason I'm not so fond of this is because I'm taking advantage of the fact that JavaScript doesn't have true protected methods, and I'm basically hacking Mongoose in order to get it to work. So I'd love to hear other answers if anyone has.
You can prevent the schema for the model from automatically generating an _id by passing in an option that sets the _id to false:
var schema = new Schema({ name: String }, { _id: false });
Source: http://mongoosejs.com/docs/guide.html#_id

Mongoose renaming _id to id

I'm creating an API that is using data from Mongoose. The front end (Backbone) expects id, instead of _id. I cannot seem to find an easy solution to such a simple problem. Is anyone aware of a way to rename _id to id. I want this to be default behavior across every schema.
Did you think of setting model.idAttribute to _id on the front end (Backbone). This would allow Backbone to 'transparently map that key to id'.
http://backbonejs.org/#Model-idAttribute
You can set up a schema method getItem which returns the desired fields and id = _id, if you really need that :)

Mongoose create custom _id from other fields

I know that Mongoose populates the _id field automatically with an ObjectID if none is given and that you can overwrite the _id when constructing and instance of the model.
What I want: create the _id from other fields in a transparent way. I want to omit the _id field when creating an instance of the model and then have a function called which fills it. This function should be declared on a Schema level and whoever uses the model does not know that _id was filled by the function instead of Mongoose.
Is there a hook or a parameter of the Schema constructor I missed?
Mongoose 3.0.x
Let's make this more concrete. Imagine a BlogPost and I want to create nice URLs by slugging the title. In order to map the slug to a Mongo Object I hash the slug and turn it into a ObjectID to leverage it's benefits. Now what I'm looking for is a transparent method which allows me to create an instance of BlogPost by only passing in title and have the slug and _id property automatically generated.
use a setter on title which slugifies and idifies for you: https://gist.github.com/3658511
If you want to make sure your code is only executed once the object is created, check for this.isNew inside the setter.
Is this what you are looking for?
You could define a function to create the _id before the model is saved, as in:
http://mongoosejs.com/docs/middleware.html
If this middleware is called after Mongoose creates the _id by default (my guess is it's not), you could tell Mongoose to not create an _id, with the _id option.
http://mongoosejs.com/docs/guide.html#options

Mongoose.js: is it possible to change name of ObjectId?

Some question about mongo ObjectId in mongoose
1) Can be ObjectId field by named not as _id? And How to do that? When I do in my code:
MySchema = new mongoose.Schema({
id : mongoose.Schema.ObjectId
});
it changes nothing.
2) If I have objectId field called _id is it possible to return from request another name for this field (for example just "id" - to send it on the in web response);
3) And question just for understanding: why is the ObjectId _id field accessible through "id" property not "_id"?
Thanks, Alex
The "_id" element is part of the mongodb architecture which guarantee that every document in a collection can be uniquely identified. This is especially important if you use sharding to allow unique identifier across disparate machine. Therefore this is a design choice so there is no way to get ride of it :)
The default value for _id are generated as follows:
timestamp
hash of the machine hostname
pid of the generating process
increment
but you can use whatever value you want as long is unique.
If it's easier for you think about the _id of something which has to be there, but you really don't care about :) Just leave the system to auto generate it and use your own identifier.
So if you still wanna create your own "id" execute something like that:
db.mySchema.ensureIndex({"id": 1}, {"unique" : true})
but make sure that is really unique and it doesn't conflict with the API you use.
2) Rename it on the application side, just before sending it as the web response.
3) I think this is because of the API you use. Maybe the author found it more logical to return the id instead of _id ? Honestly never tried mongoose :)

Resources