I was wondering , what is the best practice to keep the code DRY when developing node.js api with database calls.
I seems like I'm having alot of repeated code.
For example, look at this:
app.get('/api/users_count', function (req,res) {
pool.connect(function(err, client, done) {
if(err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT count(*) FROM users;', function(err, result) {
done();
if(err) {
return console.error('error running query', err);
}
res.json({"users count": result.rows[0].count});
});
});
});
and this:
app.get('/api/users/:id', function (req,res) {
pool.connect(function(err, client, done) {
if(err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT name FROM users WHERE id=$1;',req.param.id, function(err, result) {
done();
if(err) {
return console.error('error running query', err);
}
res.json({"user name": result.rows[0].name});
});
});
});
How can I avoid the repeating of error handing , connect call, and just focus on route and query.
Thanks!
Take a look at sample below, should help
//Create fn that connects, pulls data, and passes it to callback
function customPool(query, values, callback) {
pool.connect(function(err, client, done) {
if(err)
return callback(err);
client.query(query, values, function(q_err, result) {
done();
if(q_err)
return callback(q_err);
callback(null, result.rows);
});
}
}
//Reuse it
app.get('/api/users_count', function (req,res) {
var query = 'SELECT count(*) FROM users;';
customPool(query, undefined, function(err, rows) {
if(err)
return console.error('error fetching client from pool', err);
res.json({"users count": rows[0].count});
});
});
app.get('/api/users/:id', function (req,res) {
var query = 'SELECT name FROM users WHERE id=$1;';
customPool(query, req.params.id, function(err, rows) { //<-- notice, req.params.id not req.param.id
if(err)
return console.error('error fetching client from pool', err);
res.json({"users name": rows[0].name});
});
});
For starters, use pg-promise for database communications, to avoid connecting manually. Then your code will be way simpler, as shown below.
code 1:
app.get('/api/users_count', function (req, res) {
db.one('SELECT count(*) FROM users')
.then(data=> {
res.json({"users count": +data.count});
})
.catch(error=> {
// should provide a response here also ;)
console.error(error);
});
});
code 2:
app.get('/api/users/:id', function (req, res) {
db.one('SELECT name FROM users WHERE id=$1', +req.param.id)
.then(user=> {
res.json({"user name": user.name});
})
.catch(error=> {
// should provide a response here also ;)
console.error(error);
});
});
Then you can simplify it further, by implementing a generic request->response logic, depending on your application's requirements.
Related
I was troubleshooting why my route wasn't working and i came across this.
In my ./routes/jobs.js,
router.delete("/:id", (req, res) => {
Job.findByIdAndDelete(req.params.id, (err, job) => {
if (!err) {
res.json({ msg: "job deleted"});
} else {
console.log(err);
}
});
});
When i tested on postman, Delete - http://localhost:5000/dashboard/60b9405e1ea
Would return the id only 60b9405e1ea and not delete the db job.
I changed my route to "/" and tested it out. using http://localhost:5000/dashboard in postman.
router.delete("/", (req, res) => {
Job.findByIdAndDelete(req.params.id, (err, job) => {
if (!err) {
res.json({ msg: "job deleted"});
} else {
console.log(err);
}
});
It executed the delete request with {msg: "job deleted"}. (Obviously didnt delete db job since no id was given).
Keep in mind in my server.js im using,
app.use("/dashboard", require("./routes/jobs"));
Any help would be appreciated on why /:id is not being executed
As you are getting the id in the console, it's the problem with the query you make.
Try any of these,
Model.remove({ _id: req.body.id }, function(err) {
if (!err) {
message.type = 'notification!';
}
else {
message.type = 'error';
}
});
or
Model.findOneAndRemove({id: req.params.id}, function(err){
});
or a traditional approach:
Model.findById(id, function (err, doc) {
if (err) {
// handle error
}
doc.remove(callback); //Removes the document
})
var express = require('express');
var pg = require('pg');
var app = express();
var connectionString = "postgres://...";
app.get('/', function (req, res, next) {
pg.connect(connectionString, function (err, client, done) {
if (err) {
console.log("not able to get connection " + err);
res.status(400).send(err);
}
client.query('SELECT * FROM employee WHERE empid=$1', [1], function (err, result) {
done(); // closing the connection;
if (err) {
console.log(err);
res.status(400).send(err);
}
res.status(200).send(result.rows);
});
});
});
app.listen(3000, function () {
console.log('Server is running.. on Port 3000');
});
This is my nodejs file and the connectionString is database information that connected heroku and postgreSQL.
But when I run this one, I only get
client.query('SELECT * FROM employee WHERE empid=$1', [1], function (err, result) {
^
TypeError: Cannot read property 'query' of null
How can I solve it?
In your error handler here:
if (err) {
console.log("not able to get connection " + err);
res.status(400).send(err);
}
client.query(...)
You need to add a return so that after you send the error status, the code does not continue to try to execute client.query() because client does not have a valid value in it if there was an error. So, change to this:
if (err) {
console.log("not able to get connection " + err);
res.status(400).send(err);
return;
}
client.query(...)
Though it has less of a consequence, the same is true here:
if (err) {
console.log(err);
res.status(400).send(err);
}
res.status(200).send(result.rows);
Where you need to add a return:
if (err) {
console.log(err);
res.status(400).send(err);
return;
}
res.status(200).send(result.rows);
The overall issue in these two cases is that while res.status(...).send(...) sends the response back to the client, it does not stop your code from continuing to execute after that so you still need proper flow control with if/else or an appropriate return to control the flow of execution of the code so after an error it doesn't immediately go execute other parts of the code that you don't want it to.
I am pooling connections to my Postgres DB with pg-pool. I am having trouble, however, sending the results of my queries to the frontend. I assumed that I could use res.send as usual but it is not recognized (I also tried return). Here is my function:
exports.featList = function (req, res) {
pool.connect(function(err, client, done) {
if (err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT * FROM book WHERE featured=true', function (err,res) {
var json = JSON.stringify(res.rows);
return json;
client.release();
});
});
It seems that is not returning the results because in the callback of the client.query you are redeclaring the res variable so you can't access the response object. If you rename this variable from res to results it will be able to return the results to the client:
exports.featList = function (req, res) {
pool.connect(function (err, client, done) {
if (err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT * FROM book WHERE featured=true', function (err, results) {
var json = JSON.stringify(res.rows);
res.json(json);
client.release();
});
});
}
module.exports.validateUser = function (req, res) {
User.find({ 'username': req.body.username, 'password': req.body.password }, function (err, result) {
if (result.length) {
var gridfs = req.app.get('gridfs');
var readstream = gridfs.createReadStream({
_id: result[0]._doc.picture
});
req.on('error', function (err) {
res.send(500, err);
});
readstream.on('error', function (err) {
res.send(500, err);
});
// Just sends the file
//readstream.pipe(res);
res.send('This is incedible');
} else {
res.send(false);
}
});
};
Just after the user is validated I am getting the file associated with it. Further I want to send some data along with the fine in response. I've used multer and gridfs-stream. Is this achievable. If not, then does something looks fishy in this approach?
I am testing a nodeJS server, built on the express framework.
When a request is sent to the server, the server should send back some data, in the form of an array. Instead, it is sending back ''.
The test that I am trying to pass is as follows:
it('should send postgres data to the server', function(done) {
request(app)
.post('/chart')
.send({min:"1",max:"100",arrayLength:"12"})
.expect(200, sqlData)
.end(function(err, res){
if(err) {
done(err);
} else {
done();
}
});
});
Note that sqlData is equal to what the sent response should be.
When the router receives a POST request it does the following:
router.post('/', function(req, res) {
res.send(randomSqlTableSelector(req.body.min,req.body.max,req.body.arrayLength));
});
I have checked that req.body.min, max and arrayLength are all the numbers that I would expect them to be.
Thus, the problem is likely in my function randomSqlTableSelector, which, for whatever reason, when called inside of the router, simple returns the empty quotes ''
The function is as follows:
function randomSqlTableSelector(min,max,arrayLength) {
var filledArray = [];
pg.connect(conString, function(err, client, done) {
if(err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT cell1 FROM random AS retrievedNumber;', function(err, result) {
var psqlData = result.rows;
for (i in psqlData) {
filledArray.push(psqlData[i]["cell1"])
}
return filledArray
});
});
}
You cannot treat functions that perform asynchronous, non-blocking tasks as synchronous and blocking. Try passing in a callback instead:
function randomSqlTableSelector(min, max, arrayLength, cb) {
pg.connect(conString, function(err, client, done) {
if (err)
return cb(err);
client.query('SELECT cell1 FROM random AS retrievedNumber;', function(err, result) {
if (err)
return cb(err);
var psqlData = result.rows,
filledArray = [],
i;
for (i in psqlData)
filledArray.push(psqlData[i]["cell1"])
cb(null, filledArray);
});
});
}
Then use it in your route like:
router.post('/', function(req, res) {
var min = req.body.min,
max = req.body.max,
len = req.body.arrayLength;
randomSqlTableSelector(min, max, len, function(err, array) {
if (err) {
console.log(err);
return res.send(500);
}
res.send(array);
});
});