I'm trying to add a function that deletes a record in MongoDB by the id, but I'm getting an empty array as a result and the record is not deleted.
Here is my code so far:
//router
router.delete('/comandas/:id', (req, res) => {
deleteLine(req.params.id)
res.status(500).end()
});
});
//delete function
const objectId = require('mongodb').ObjectID;
const init = () =>
MongoClient.connect(connectionUrl, { useNewUrlParser: true, useUnifiedTopology: true }).then((client) => {
db = client.db(dbName)
})
const deleteLine = (id) => {
const collection = db.collection('comanda')
return collection.deleteOne({"_id": objectId(id)})
}
You are returning a promise in deleteLine function, in your router to actually make it run you need to add then block like this:
deleteLine(req.params.id).then(result => {
console.log(result);
//todo: send response
})
.catch(err => {
console.log(err);
//todo: send error response
})
I found another way to solve this calling the get methods instead:
router.get('/comandas/:id/delete', (req, res) => {
deleteLine(req.params.id)
.then( () => {
console.log(req.params.id + ' deleted')
res.status(500).end()
})
.catch((err) => {
console.log(err)
res.status(500).end()
});
});
Related
I am trying to filter all objects in the projects array that are not in the savedProjects array but the result is an empty array. Am I filtering the wrong way?
My code:
router.post('/get-related', async (req, res) => {
console.log(req.body);
try {
const projects = await Projects.find({researchers: req.body.fullname});
let savedProjects = await Projects.find({projectID: req.body.projectsIDs});
projects.filter(p => savedProjects.includes(p) === false);
res.json(projects);
} catch (err) {
console.log(err);
res.json({ message: err }) };
});
Array filter function returns a new array without modifying the original so for this to work I think something like this would be enough:
let filteredProjects = projects.filter(p => savedProjects.includes(p) === false); res.json(filteredProjects);
more info here, MDN Array.filter()
Using the $nin specification fixed it. I imported the saved projects and then queried all projects that did not include the ones in the savvedProjects array.
router.post('/get-related', async (req, res) => {
try {
const savedProjects = await Projects.find({projectID: req.body.projectsIDs});
let IDs = savedProjects.map(p => p.projectID);
const projects = await Projects.find({ category: req.body.researcharea, projectID: { $nin: IDs }});
res.json(projects);
} catch (err) { res.json({ message: err }) };
});
const suggestedProjects = projects.filter(p => savedProjects.some((p)=> p=== false);
I have 3 tables (services, practitioners, network) in my postgres database and I want them all to show in my API, but I got this error
(node:21300) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
and this is the only output I could get
json response
here is my code.
const handler = (req, res, db, tableName) => {
try{
db.select('*').from(tableName)
.then(data => {
if(data.length){
res.status(200).json({tableName: data});
}
})
}catch(err){
res.status(400).json(err)
}
}
const content = (req, res, db) => {
handler(req, res, db, 'services')
handler(req, res, db, 'practitioners')
}
module.exports = { content };
edit:
here's what I did from Nabil Farhan's answer, and it's working just what I wanted. screenCapture
const getData = async (db, tableName) => {
return new Promise((resolve, reject) => {
db.select('*').from(tableName)
.then(data => {
resolve({[tableName]:data})
})
});
}
const contentHandler = async (req, res, db) => {
// get the argument from get request
let allTable = [];
const table_a = await getData(db, 'services');
const table_b = await getData(db, 'practitioners');
allTable.push(table_a);
allTable.push(table_b);
res.status(200).json({data: allTable});
}
module.exports = { contentHandler };
I recommend you to use promise like this:
async function getData(tableName) {
return new Promise(function (resolve, reject) {
db.select("*")
.from(tableName)
.then((data) => {
resolve(data);
});
});
}
async function run(){
var allTable = [];
const firstTable = await getData('testingDB');
const secondTable = await getData('user');
allTable.push(firstTable);
allTable.push(secondTable);
res.status(200).json({data: allTable});
}
run().then(() => {
console.log("Finished!");
})
I see a couple potential issues here.
Unhandled Promise rejection:
Add a catch block after then (you can rethrow to put it in the existing catch block).
.then(data => {
if(data.length){
let responseData = {}
responseData[tableName] = data
res.status(200).json(responseData)
}
})
.catch(err) {throw err}
tableName is also not going to be interpreted as variable, but as a literal property name, so I changed it to get what I think you were going for.
For the header error, you are setting the response twice, once for "services", then again for "practitioners". One possible solution is to remove the res.status... line from handler and use Promise.all in content and moving the response setting there:
const content = (req, res, db) => {
Promise.all(
handler(req, res, db, 'services')
handler(req, res, db, 'practitioners')
)
.then(allData => {
//use reduce/Object.assign to combine the individual data objects
allData.reduce((finalResponseData, data) => {
if(!data.length) return finalResponseData
Object.assign(finalResponseData, data)
})
.then(finalResponseData => res.status(200).json(finalResponseData)
.catch(err) {res.status(400).json(err)}
}
I'm trying to create an Update API route using Sequelize that will:
Capture the record before the update (beforeRec)
Perform the update
Capture the updated record (updatedRec)
Return both the beforeRec and updatedRec
I'm having trouble with my promise chain, which is executing the before and after select queries before executing the update. I've tried several different ways of chaining and capturing results, but here's the latest code:
router.put('/:id', (req, res) => {
const pk = req.params.id;
const getBeforeRec = Master.findByPk(pk)
.then(rec => {return rec})
const updateRec = getBeforeRec
.then(
Master.update(
req.body,
{ where: {id: pk} }
)
)
const getUpdatedRec = updateRec
.then(
Master.findByPk(pk)
.then(rec => {return rec})
);
return Promise.all([getBeforeRec, updateRec, getUpdatedRec])
.then( ([beforeRec, updateRes, afterRec]) => {
return res.json({beforeRec, afterRec})
})
.catch(err => {
return res.status(400).json({'error': err});
});
});
Here's a sanitized example of how the results look:
{
"beforeRec": {
"id": 100,
"updated_col_name": false,
},
"afterRec": {
"id": 100,
"updated_col_name": false,
}
}
In the console, I can see that the update is executing last:
Executing (default): SELECT [id], [updated_col_name] FROM [master] WHERE [master].[id] = N'100';
Executing (default): SELECT [id], [updated_col_name] FROM [master] WHERE [master].[id] = N'100';
Executing (default): UPDATE [master] SET [updated_col_name]=1 WHERE [id] = N'106'
What's the best way to make the second select statement wait for the update?
Any help in clarifying how to chain promises while capturing results along the way will be greatly appreciated! Thanks.
After trying a number of ways, it finally works with nesting:
router.put('/:id', (req, res) => {
const pk = req.params.id;
let beforeRec;
Master.findByPk(pk)
.then(rec => { beforeRec = rec; })
.then(() => {
Master.update(
req.body,
{ where: {id: pk} }
)
.then(() => {
Master.findByPk(pk)
.then(rec => { return rec; })
.then((afterRec) => {
return res.json({beforeRec, afterRec})
})
})
})
.catch(err => {
return res.status(400).json({'error': err});
});
});
If I don't nest the second Master.findByPk, then Master.update() ends up executing last. Also, while I can set beforeRec outside of the promise chain, it didn't work for afterRec.
I don't love it, since I'm still confused by promises, but it's returning the desired results. However, with this nesting mess, I'm not sure where the catch() belongs. Will it catch errors within the nested then()s? Only further testing will tell.
You can do that with , previous method of the instance that returned by update query :
Master.update( req.body , { where: {id: pk} }).then(master => {
console.log(master.get()); // <---- Will give you latest values
console.log(master.previous()); // <---- returns the previous values for all values which have changed
})
For More Detail :
http://docs.sequelizejs.com/class/lib/model.js~Model.html#instance-method-previous
https://github.com/sequelize/sequelize/issues/1814
Give this a shot:
router.put('/:id', (req, res) => {
const pk = req.params.id;
let beforeRec, afterRec;
Master.findByPk(pk)
.then(rec => { beforeRec = rec; })
.then(() => {
Master.update(
req.body,
{ where: {id: pk} }
)
})
.then(() => {
Master.findByPk(pk)
.then(rec => { afterRec = rec; })
})
.then(() => {
res.json({beforeRec, afterRec})
})
.catch(errror => {
res.status(400).json({error});
});
});
Resurrecting an old question to help people in the future...
I've been using sequelize v6 with MySQL. I can't speak to other variances but assuming you just want the snapshot of the "previous" values, you can use the following method to create a copy the properties and their values before updating them
// then catch method
router.put('/:id', (req, res) => {
const pk = req.params.id;
let beforeRecord;
const updateRec = Master.findByPk(pk).then(rec => {
// .get() method is synchronous
beforeRecord = rec.get({ plain: true });
// calling .update on the model instance will also
// call .reload on the instance as well.
// Same thing happens when calling .save on the instance
return rec.update(req.body);
});
updateRec.then(rec => {
const afterRec = rec.get({ plain: true });
return res.json({beforeRec, afterRec})
}).catch(err => {
return res.status(400).json({'error': err});
});
});
// Async await method
router.put('/:id', async (req, res) => {
const pk = req.params.id;
try {
/** #type{import('sequelize').Model} */ // rec equals a sequelize model instance
const rec = await Master.findByPk(pk)
// .get() method is synchronous and returns an object (NOT a sequelize model instance)
const beforeRecord = rec.get({ plain: true });
// calling .update on the model instance will also
// call .reload on the instance as well.
// Same thing happens when calling .save on the instance
await rec.update(req.body); // after this call, rec contains the new updated values
const afterRec = rec.get({ plain: true });
return res.json({beforeRec, afterRec})
} catch (err) {
return res.status(400).json({'error': err});
}
});
I have the following routes methods in my controller:
getListaUsuarios() {
this.app.get("/api/usuario", (req, res) => {
this.usuario.getListaUsuarios().then((results) => {
return res.status(200).json(results);
}).catch((error) => {
return res.status(500).send(error);
});
});
}
getUsuarioByEmail() {
this.app.get("/api/usuario/:usuarioEmail", (req, res) => {
let usuarioEmail = req.params.usuarioEmail;
this.usuario.getUsuarioByEmail(usuarioEmail).then((results) => {
if(!results) return res.status(404).json();
return res.status(200).json(results);
}).catch((error) => {
return res.status(500).send(error);
});
});
}
My question is related with the routes, for best practices we know that we should be using the name of the resources, in the first method I'm retrieving the list of Users and in the second I'm trying to retrieve the user by email. But when I try to call the api with: localhost:3000/api/usuario?usuarioEmail=xxxx#xxx.com it always call the first method and not the second. Is there something wrong with the way I'm defining my routes, or I have to change the complete path always.
In the url localhost:3000/api/usuario?usuarioEmail=xxxx#xxx.com, usuarioEmail is a query string; your rout is expecting it as a parameter. The correct usage given your routes would be:
localhost:3000/api/usuario/xxxx%40xxx.com
Where %40 represents the URI encoding of #.
If you actually wanted to use query strings, you would need to do something like this:
getListaUsuarios() {
this.app.get("/api/usuario", (req, res) => {
if (!req.query.usarioEmail) { // check for the query string
let usuarioEmail = req.params.usuarioEmail;
this.usuario.getUsuarioByEmail(usuarioEmail).then((results) => {
if(!results) return res.status(404).json();
return res.status(200).json(results);
}).catch((error) => {
return res.status(500).send(error);
});
} else {
this.usuario.getListaUsuarios().then((results) => {
return res.status(200).json(results);
}).catch((error) => {
return res.status(500).send(error);
});
}
});
}
Apparently you are mixing req.params with req.query
req.query
Works when you want to use an url just like the one you posted
localhost:3000/api/usuario?usuarioEmail=xxxx%40xxx.com
and the logic has to be setup in the /api/usuarios route
getListaUsuarios() {
this.app.get("/api/usuario", (req, res) => {
if (req.query.usuarioEmail) { //Aply your logic over here
/*If wont change the value of usuarioEmail, you should use
const instead of let*/
const usuarioEmail = req.params.usuarioEmail;
return this.usuario.getUsuarioByEmail(usuarioEmail).then((results) => {
if(!results) return res.status(404).json();
return res.status(200).json(results);
}).catch((error) => {
return res.status(500).send(error);
});
}
this.usuario.getListaUsuarios().then((results) => {
return res.status(200).json(results);
}).catch((error) => {
return res.status(500).send(error);
});
});
}
req.params
Is used when you want to do something like this
localhost:3000/api/usuario/micorreo%40email.com
And your route will look exactly like you have defined it
/api/usuario/:usuarioEmail
The value of :usuarioEmail is in req.params.usuarioEmail
If you want to know more about the req object, here's a link to the Express documentation
router.get('/', (req, res) => {
User.find({}, (err, users) => {
res.render('users/index', {users: users});
});
));
There is no problem with the data being rendered on the page.
I am just looking for a way to make an assertion on the {users: users}
Something like the following...
// testfile.js
const user = {name: 'nate'}
beforeEach((done) => {
User.remove({}).then(() => {
return User.insert(user);
}).then(() => done());
});
it('should GET /users page', (done) => {
request(app)
.get('/users/')
.end((err, response) => {
assert(response.body === user);
done();
})
});
Is there a technique for accessing an object that was passed to a page?
Or possibly a library or for this type of assertion?