TypeError: Cannot read property '_id' of undefined at insertDocuments at insertOne - mongodb

I was trying to insert a record into a collection when this error was thrown. I went through the mongodb doc on insertOne and I understand that mongod will automatically add the _id when it is not specified as in my case, so I'm wondering why there is an undefined id error.
Here's the code I'm working with, first the api route and the data I'm trying to insert
app.post('/api/contacts', (req, res) => {
// retrieve the user being added in the body of the request
const user = req.body;
// obtain a reference to the contacts collection
const contactsCollection = database.collection('contacts');
// insert data into the collection
contactsCollection.insertOne(user, (err, r) => {
if (err) {
return res.status(500).json({error: 'Error inserting new record.'});
}
const newRecord = r.ops[0];
return res.status(201).json(newRecord);
});
});
The json data for inserting
{
"name": "Wes Harris",
"address": "289 Porter Crossing, Silver Spring, MD 20918",
"phone": "(862) 149-8084",
"photoUrl": "/profiles/wes-harris.jpg"
}
Database connection to the to the database hosted on mlab is successful with no errors. What could possibly be wrong here and how do I go about fixing this error?

The error message means that the object that you are passing into insertOne is undefined (and hence it can’t read the _id property of of it). You might like to look at what exactly req.body contains as that is what is passed as user. (I don’t know TypeScript but, in node/express, I have had errors like this when I didn’t set up bodyParser correctly)

Faced a similar problem. Solved and it by using body-parser and then parsing the json object being sent as follows:
const bodyParser = require('body-parser');
app.use(bodyParser.json());
// parse application/json
app.use(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.write('you posted:\n')
res.end(JSON.stringify(req.body, null, 2))
})

Related

dealing with promises returned from mongoose

This is probably a simple concept to some but an understanding of promises is something that I continue to struggle with...
I have a simple web app that connects to a mongo DB using mongoose and an MVC pattern. I used this tutorial to get up and running but I am not looking to build an API just a back end for my application. I believe interfacing with the controller is where I need some help understanding...
My App structure is as follows
db.congif.js - DB connection managed here
movie.model.js - this is where my object schema is defined
movie.contrller.js - this is where the db operations are written
index.js - main file for my app
Here is the example controller
exports.create = (req, res) => {
// Validate request
if (!req.body.title) {
res.status(400).send({ message: "Content can not be empty!" });
return;
}
// Create a Tutorial
const tutorial = new Tutorial({
title: req.body.title,
description: req.body.description,
published: req.body.published ? req.body.published : false
});
// Save Tutorial in the database
tutorial
.save(tutorial)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the Tutorial."
});
});
};
I want to update this to accept an object and return a "response" here begins my lack of understanding
exports.create = (tutorialObject) => {
// Save Tutorial in the database
tutorial
.save(tutorialObject)
.then(data => {
return data;
})
.catch(err => {
return {
message:
err.message || "Some error occurred while creating the Tutorial."
};
});
};
and finally how I am calling this
let dbResponse = tutorial.create(
{
original_title : original_title,
title : title,
poster_path : poster_path
})
So my question... Please help me understand the correct way to code\call this. Is the controller code correct, am I calling it correctly? I am not sure because even though this is working to write records to the DB the dbResponse is always undefined.
The code works to write records to the DB but the response from the controller is undefined. I would expect to have the response be the record that was inserted?

POST collection of objects in json-server

I am using json-server to fake the Api for the FrontEnd team.
We would like to have a feature to create the multiple objects (Eg. products) in one call.
In WebApi2 or actual RestApis, it can be done like the following:
POST api/products //For Single Creation
POST api/productCollections //For Multiple Creation
I don't know how I can achieve it by using json-server. I tried to POST the following data to api/products by using the postman, but it does not split the array and create items individually.
[
{
"id": "ff00feb6-b1f7-4bb0-b09c-7b88d984625d",
"code": "MM",
"name": "Product 2"
},
{
"id": "1f4492ab-85eb-4b2f-897a-a2a2b69b43a5",
"code": "MK",
"name": "Product 3"
}
]
It treats the whole array as the single item and append to the existing json.
Could you pls suggest how I could mock bulk insert in json-server? Or Restful Api should always be for single object manipulation?
This is not something that json-server supports natively, as far as I know, but it can be accomplished through a workaround.
I am assuming that you have some prior knowledge of node.js
You will have to create a server.js file which you will then run using node.js.
The server.js file will then make use of the json-server module.
I have included the code for the server.js file in the code snippet below.
I made use of lodash for my duplicate check. You will thus need to install lodash. You can also replace it with your own code if you do not want to use lodash, but lodash worked pretty well in my opinion.
The server.js file includes a custom post request function which accesses the lowdb instance used in the json-server instance. The data from the POST request is checked for duplicates and only new records are added to the DB where the id does not already exist. The write() function of lowdb persists the data to the db.json file. The data in memory and in the file will thus always match.
Please note that the API endpoints generated by json-server (or the rewritten endpoints) will still exist. You can thus use the custom function in conjunction with the default endpoints.
Feel free to add error handling where needed.
const jsonServer = require('json-server');
const server = jsonServer.create();
const _ = require('lodash')
const router = jsonServer.router('./db.json');
const middlewares = jsonServer.defaults();
const port = process.env.PORT || 3000;
server.use(middlewares);
server.use(jsonServer.bodyParser)
server.use(jsonServer.rewriter({
'/api/products': '/products'
}));
server.post('/api/productcollection', (req, res) => {
const db = router.db; // Assign the lowdb instance
if (Array.isArray(req.body)) {
req.body.forEach(element => {
insert(db, 'products', element); // Add a post
});
}
else {
insert(db, 'products', req.body); // Add a post
}
res.sendStatus(200)
/**
* Checks whether the id of the new data already exists in the DB
* #param {*} db - DB object
* #param {String} collection - Name of the array / collection in the DB / JSON file
* #param {*} data - New record
*/
function insert(db, collection, data) {
const table = db.get(collection);
if (_.isEmpty(table.find(data).value())) {
table.push(data).write();
}
}
});
server.use(router);
server.listen(port);
If you have any questions, feel free to ask.
The answer marked as correct didn't actually work for me. Due to the way the insert function is written, it will always generate new documents instead of updating existing docs. The "rewriting" didn't work for me either (maybe I did something wrong), but creating an entirely separate endpoint helped.
This is my code, in case it helps others trying to do bulk inserts (and modifying existing data if it exists).
const jsonServer = require('json-server');
const server = jsonServer.create()
const _ = require('lodash');
const router = jsonServer.router('./db.json');
const middlewares = jsonServer.defaults()
server.use(middlewares)
server.use(jsonServer.bodyParser)
server.post('/addtasks', (req, res) => {
const db = router.db; // Assign the lowdb instance
if (Array.isArray(req.body)) {
req.body.forEach(element => {
insert(db, 'tasks', element);
});
}
else {
insert(db, 'tasks', req.body);
}
res.sendStatus(200)
function insert(db, collection, data) {
const table = db.get(collection);
// Create a new doc if this ID does not exist
if (_.isEmpty(table.find({_id: data._id}).value())) {
table.push(data).write();
}
else{
// Update the existing data
table.find({_id: data._id})
.assign(_.omit(data, ['_id']))
.write();
}
}
});
server.use(router)
server.listen(3100, () => {
console.log('JSON Server is running')
})
On the frontend, the call will look something like this:
axios.post('http://localhost:3100/addtasks', tasks)
It probably didn't work at the time when this question was posted but now it does, call with an array on the /products endpoint for bulk insert.

Express - return certain documents with named route parameters using axios

I'm having trouble communicating between the frontend and backend for a selected GET request.
I am using a React frontend with an express/mongoose setup out in the backend.
In the frontend, I do a GET call using axios for:
axios.get('/api/orders/', {
params : {
name: this.props.user.name // user name can be Bob
}
})
And in the backend I'm having a hard time understanding the correct method I would need to do to query the database (example below doesn't work). I found stuff with .select but even then I still can't get it to work:
router.get('/orders', function(req, res) {
Order.find({}).select(req.params).then(function (order) {
res.send(req.params);
})
});
I also tried doing this to see if I can even get the params to send properly and to no demise:
router.get('/orders/:name', function(req, res) {
res.send('client sent :',req.query.name);
});
The orders document model holds objects that house an ordered array and a name (type: String) attached to the object. The Mongoose scheme for the order:
const orderScheme = new Schema({
name : { type : String },
orders : { type : Array}
});
In my MongoDB, I can see all the "Master Orders" send back. Each master order has the name of who submitted it, plus all the orders within (there can be a ton of orders).
What I'm trying to exactly do is pull up all orders that have a certain name. So if I search "TestAccount", I'll get all of bob's orders. I've included an image below:
Any pointers?
Client-side:
axios.get('/api/orders/' + this.props.user.name)
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
You need to handle the Promise when resolved/rejected.
Server-side:
router.get('/orders/:name', function(req, res) {
return Order.find({name: req.params.name}).then(function(orders) {
// return orders when resolved
res.send(orders);
})
.catch(function (error) {
// handle error
console.log(error);
})
});
You did not specify a named route parameter in your route path.
You also aren't accessing the name property by using req.params only.
You should use Model.find() conditions parameter to specify which document[s] you're trying to find. Query.prototype.select() is for filtering document fields.

TypeORM FindById doesn't work with MongoDB

I'm trying to use TypeORM with MongoDB and express but I'm having problems with the basic stuff.
I just created a controller with basic CRUD operations for an entity. The methods save, findAll and find by Filter works ok, but I can't make the methods that require an mongo id work.
router.get("/", async(req: Request, res: Response) => {
const investmentRepository = getMongoRepository(Investment);
const investments = await investmentRepository.find();
res.send(investments);
});
router.get("/:id", async(req: Request, res: Response) => {
const investmentRepository = getMongoRepository(Investment);
const investment = await
investmentRepository.findOneById(req.params.id);
if (!investment) {
res.status(404);
res.end();
}
res.send(investment);
});
The second method is always returning 404.
For example, this is an entity returned on get all "investment/"
{
"id": "59dfd8cadcbd9d1720457008",
"name": "Teste LCI",
"startDate": 1466305200,
"numberOfDays": 365,
"type": "LCI_LCA"
}
If I try to send a request for this specific object calling
investment/59dfd8cadcbd9d1720457008
the response is always 404.
The same behavior happen with the delete method, raising an exception
Cannot find entity to remove by a given id
I also tried to convert the string to ObjectID using:
new ObjectID(req.params.id);
but it fails with the error ObjectID is not a constructor.
If you're receiving the error ObjectId is not a constructor it is because you forgot to require it in your file. All you need is:
const ObjectId = require('mongodb').ObjectId;

Express JS routing based on the user names

I am working on a express js project.I have got all my basic routing set up working perfectly. Usually when I want to search a record based on id I do this:
router.route('/sensors_home/:sensor_id')
.get(function (req, res) {
Sensor.findById(req.params.sensor_id,function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});
This allows me to retrieve the data when I do http://localhost:4000/sesnors_home/45000cbsfdhjbnabfbajhdb
(45000cbsfdhjbnabfbajhdb = Object id from the MongoDB )
Now my goal is to have several users to my application. I have my mongoose schema set up and the mongoDB looks like this :
Here is the issue: I wanna retrieve data corresponding to John Peterson based on his _id that is "John".Instead of doing this http://localhost:4000/sesnors_home/45000cbsfdhjbnabfbajhdb I wanna do something like this http://localhost:4000/sesnors_home/John and retrieve all the data specific to John. I tried various methods but still stuck with this issue. I tried using req.params._id and also some Mongodb queries on the User Collection but still no luck. Please suggest some ideas.
Thanks!
UPDATE:
I tried using the following code :
router.route('/sensors_home/:id')
.get(function (req, res) {
res.send(_id.toString());
User.findOne({_id: req.params._id} ,function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});
This gives me the following error :
ReferenceError: _id is not defined
Have you tried the following?
router.route('/sensors_home/:_id')
.get(function (req, res) {
Sensor.findOne({_id: req.params._id},function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});