Express Route Parameters creating duplicate custom paths - node.js

I am creating a to-do list app and I am trying to including custom routes using 'Express Route Parameters'. The program worked fine up until this point, but as soon as I tried to introduce these custom paths and log it to the console, or even add it to the database, the very first path gets added twice or logged twice. The paths added later are not duplicated, though.
app.get("/:customListName", function(req,res)
{
console.log(req.params.customListName);
/*const list = new List
({
name: customListName,
items: defaultItems
});
list.save();*/
});
For example, if the custom path added is called "home", i.e., "localhost:3000/home" and I am trying to console.log the name of the path, it will print "home" twice. Later, if I add paths like "work", "new" etc they are added (and printed) only once. Sometimes this error is also coming up:
BulkWriteError: E11000 duplicate key error collection: wolDB.items index: _id_ dup key: { _id: ObjectId('5ef4ad2110f45d54f143fa19') }
I have tried dropping the database and start afresh, drop indexes, even tried with a new database; but it seems the problem is not in the database because even without pushing it to the database, the problem persists. I tried coding the whole thing afresh, but the problem persists.
I have tried findOne() of mongoose also, but when I try to print whether the given route exists or not, for the first one it just prints "exists" twice.
List.findOne({name: customListName}, function(err, foundList)
{
if(!err)
{
if(!foundList)
console.log("Doesn't exist");
else
console.log("Exists");
}
});
Here's the GitHub link:
https://github.com/sebanti10/todolist.git

if you don't need the list and want to directly save to the database try using
List.create({
name: customListName,
items: defaultItems
})

Look at your schemas. Nowhere are you using new mongoose.Schema(). When you use mongoose.model("Item", itemsSchema) you need to pass in an actual schema, not just any object.
You need to change your so called schemes to something along the lines of:
const itemsSchema = new Schema({
name: String
});
Schema is available under mongoose, so you can either use mongoose.Schema directly or grab it const Schema = mongoose.Schema; or const { Schema } = mongoose;

Related

Validate relationship existance in MongoDB with Mongoose

I'm using Mongoose and its not in an advanced stage, so I need some help with some specific points. I will try to keep my examples clear and without much context for it.
First of all, I'm doing some relationships in my schemas. Before I create or edit any of them, I'm verifying if the provided ObjectId exists in database when necessary.
VehicleSchema = new Schema({
name: String,
})
PersonSchema = new Schema({
name: String,
vehicle: ObjectId //relation with vehicle
})
PersonSchema.pre('save', (next) => {
// ...
if (!Vehicles.countDocuments({ _id: this.vehicle }) throw new Error('blabla')
// ...
}
Is there any better way to do this or is this the best way possible to make sure that my doc exists?
I was thinking about three possibilities to help this be faster, but I'm not sure if is secure and consistent:
Create a custom ObjectId that indicates the modelName of my Schema in it. Something like:
function createObjectIdByModelName(modelName) {
return new ObjectId(`${modelName}-${uuid.v4()}`)
}
and then:
function validateObjectIdByModelName(_id, expectedModel) {
const modelName = mongoose.model.get(_id).modelName
return modelName === expectedModel
}
Use some cache package like recachegoose or speedgoose
Make my requests have an "origin" where I could create some rules like:
// This is a simple example of course, but the idea is that
// if the origin of my request is my frontend, I would trust in it, so my validation
// would be ignored. Otherwise I validate it normally.
if (origin !== 'frontend') {
if (!Vehicles.countDocuments({ _id: this.vehicle }) throw new Error('blabla')
}
What do you think about? This is blowing my mind for weeks now.

using spread syntax with Mongoose Document after calling the .save method results in undefined keys

I'm using a Mongoose/MongoDB and I'm getting some odd behaviour when I try to use the spread syntax to return values from a document after I call .save() on it.
// Npc is a Mongoose schema
const npc = new Npc({
...input,
creator: userId
});
const createdNpc = await npc.save();
I have tried using the spead operator, but the name and description keys do not exist.
return {
...createdNpc
creator: userFromId(npc.creator)
}
however when I access those values directly they ARE defined
return {
description: createdNpc.description,
name: createdNpc.name,
creator: userFromId(npc.creator)
};
I've made sure that the spelling of description and name are correct. I've tried logging both {...createdNpc} and {...createdNpc, description: createdNpc.description, name: createdNpc.name}. In the logs I've confirmed that name and description are both not defined (the keys don't exist) inside of {...createdNpc}
I have also tried logging createdNpc and {...createdNpc} and have confirmed that they return different values.
here's createdNpc:
{
_id: 5d8d5c7a04fc40483be74b3b,
name: 'NPC Name',
description: 'My Postman NPC',
creator: 5d8d50e0b5c8a6317541d067,
__v: 0
}
it doesn't actually look like a Mongoose Document at all. I would post the result of {...createdNPC} to show the difference but it's a huge code snippet and I don't want to clutter the question. I'm happy to provide it if it will help!
I'm still very new to MongoDB & Mongoose. Why would using the spread syntax on a Mongoose Document change its value?
I don't think this should be relevant to the question but just in case I'll also mention this is for a graphql resolver.
This is because Mongoose uses getters for all of its attributes. Before you use the spread operator, call createdNpc.toObject() to get a normal Object.

node not recognizing duplicated entries

I'm trying to create a basic MEAN stack CRUD api to add shops into my database. I want every shop to have a unique name (to avoid adding duplicates). So far, everything gets saved into the database even if I post the same request 10 times. Went trough the code a couple of times and can't figure out what's wrong, if anyone could point me in the right direction I'd be very grateful.
shop model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');
//shop schema
var ShopSchema = new Schema({
name: { type: String, required: true, index: { unique: true }},
address: { type: String, required: true, index: { unique: true }}
});
module.exports = mongoose.model('Shop', ShopSchema);
post function:
apiRouter.route('/shops')
//create a shop
.post(function(req, res) {
//new instance of shop model
var shop = new Shop();
//set the shop information
shop.name = req.body.name;
shop.address = req.body.address;
//save shop and check for errors
shop.save(function(err) {
if(err) {
//duplicate entry
if(err.code == 11000) {
return res.json({ success: false, message: 'A shop with that name already exists.'});
}
else {
return res.send(err);
}
}
else {
res.json({ message:'Shop created! '});
}
});
})
I do not receive errors of any kind, like I said everything just gets written into the database.
Thanks for the help.
Basically your writes haven't finished before the new entries are saved. You can read more about creating unique keys Here, but the gist is below. The solution is to create an index over the unique fields ahead of time.
When we declare a property to be unique, we’re actually declaring that we want a database-level index on that property. Some database abstraction layers will issue a query to see if a there’s another record with the same value for the unique property, and if that query comes back empty, it allows the save or update to proceed. If you trust this method, you either have incredibly low traffic or you’re about to learn about race conditions, because 2 or more requests could have their checks to the database occur before any writes go out, and you end up with non-unique data in your DB.
In between the time that check query is issued, another insert could come along doing the exact same thing, and you still end up with duplication. Uniqueness can’t be correctly validated at the application level. So it’s good that Mongoose tries to create an index for us.

Adding fields to model which derived from Mongoose schema

I have a Mongoose schema that looks like this:
ManifestSchema = new Schema({
entries: [{
order_id: String,
line_item: {}, // <-- resolved at run time
address: {},// <-- resolved at run time
added_at: Number,
stop: Number,
}]
}, {collection: 'manifests', strict: true });
and somewhere in the code I have this:
Q.ninvoke(Manifests.findById(req.params.id), 'exec')
.then(function(manifest)
{
// ... so many things, like resolving the address and the item information
entry.line_item = item;
entry.address = order.delivery.address;
})
The issue that I faced is that without defining address and line_item in the schema, when I resolved them at run time, they wouldn't returned to the user because they weren't in the schema...so I added them...which cause me another unwanted behavior: When I saved the object back, both address and line_item were saved with the manifest object, something that I would like to avoid.
Is there anyway to enable adding fields to the schema at run time, but yet, not saving them on the way back?
I was trying to use 'virtuals' in mongoose, but they really provide what I need because I don't create the model from a schema, but it rather returned from the database.
Call toObject() on your manifest Mongoose instance to create a plain JavaScript copy that you can add extra fields to for the user response without affecting the doc you need to save:
Q.ninvoke(Manifests.findById(req.params.id), 'exec')
.then(function(manifest)
{
var manifestResponse = manifest.toObject();
// ... so many things, like resolving the address and the item information
entry.line_item = item;
entry.address = order.delivery.address;
})

Mongoose key/val set on instance not show in JSON or Console.. why?

I have some information on my mongoose models which is transient. For performance reasons I dont wish to store it against the model.. But I do want to be able to provide this information to clients that connect to my server and ask for it.
Here's a simple example:
var mongoose = require('mongoose'),
db = require('./dbconn').dbconn;
var PersonSchema = new mongoose.Schema({
name : String,
age : Number,
});
var Person = db.model('Person', PersonSchema);
var fred = new Person({ name: 'fred', age: 100 });
The Person schema has two attributes that I want to store (name, and age).. This works.. and we see in the console:
console.log(fred);
{ name: 'fred', age: 100, _id: 509edc9d8aafee8672000001 }
I do however have one attribute ("status") that rapidly changes and I dont want to store this in the database.. but I do want to track it dynamically and provide it to clients so I add it onto the instance as a key/val pair.
fred.status = "alive";
If we look at fred in the console again after adding the "alive" key/val pair we again see fred, but his status isnt shown:
{ name: 'fred', age: 100, _id: 509edc9d8aafee8672000001 }
Yet the key/val pair is definitely there.. we see that:
console.log(fred.status);
renders:
alive
The same is true of the JSON representation of the object that I'm sending to clients.. the "status" isnt included..
I dont understand why.. can anyone help?
Or, alternatively, is there a better approach for adding attributes to mongoose schemas that aren't persisted to the database?
Adding the following to your schema should do what you want:
PersonSchema.virtual('status').get(function() {
return this._status;
});
PersonSchema.virtual('status').set(function(status) {
return this._status = status;
});
PersonSchema.set('toObject', {
getters: true
});
This adds the virtual attribute status - it will not be persisted because it's a virtual. The last part is needed to make your console log output correctly. From the docs:
To have all virtuals show up in your console.log output, set the
toObject option to { getters: true }
Also note that you need to use an internal property name other than status (here I used _status). If you use the same name, you will enter an infinite recursive loop when executing a get.
Simply call .toObject() on the data object.
For you code will be like:
fred.toObject()
This has been very helpful. I had to struggle with this myself.
In my case, I was getting a document from mongoose. When I added a new key, the key was not visible to the object if I console.log it. When I searched for the key (console.log(data.status), I could see it in the log but not visible if I logged the entire object.
After reading this response thread, it worked.
For example, I got an object like this one from my MongoDB call:
`Model.find({}).then(result=> {
//console.log(result);// [{ name:'John Doe', email:'john#john.com'}];
//To add another key to the result, I had to change that result like this:
var d = result[0];
var newData = d.toJSON();
newData["status"] = "alive";
console.log(newData);// { name:'John Doe', email:'john#john.com', status:'alive'};
}).catch(err=>console.log(err))`
Hope this helps someone else.
HappyCoding

Resources