Optimize API requests in Node.js - javascript

I have an API in Node.js. My routes look like
exports.getUserList = (req, res, next) => {
User.find().sort({ name: 1 }).then(users => {
res.json({
status: 'success',
users
});
}).catch(next);
};
As seen in the example, I use .catch(next). But is this the correct way to do it? Shouldn't the route always print json?
So I am thinking of doing something like
exports.getUserList = (req, res, next) => {
User.find().sort({ name: 1 }).then(users => {
res.json({
status: 'success',
users
});
}).catch(err => {
res.json({
status: 'error',
msg: err
});
});
};
but shouldn't it then be something like res.status(some_status_code).json({})?
How is a simple API normally carried out in terms of error handling?
What if I, in the code, use a variable that is not defined (i.e. causing a syntax error)? Should I handle it with a JSON error or should I just make sure that I don't do sloppy coding? :-D
Also, is this the fastest way to print the json? I mean, should I use User.find().lean()? Should I do some caching? Is it even clever to store my API on a normal website or are there optimized API servers for such cases?

Have you try async/await function and custom response for success and error?
Here the example :
responseHandler.js
// use this for success
function successResponse(message, data) {
const success = {
success: true,
message: message,
data: data
}
return success
}
// use this as middleware error handler
function errorResponse(err,req,res,next) => {
return res.status(err.status || 500).json({
success: false,
message: err.message
})
}
module.exports = {
successResponse,
errorResponse
}
myRouter.js
const { successResponse } = require('./responseHandler')
exports.getUserList = async (req, res, next) => {
await User.find().sort({ name: 1 }).then(users => {
res.status(200).json(
successResponse(`data user`, users)
)
}).catch(err => {
return err
});
};

Related

How to deal with async functions in express js router

There is a lint error, that basically says that we cannot return Promises in places where a void is expected, the message is clear, but how to fix it without lying to the linter by using any or (as any), both functions [validateJWT, getUser] are async functions It looks pretty basic, but I do not know how to fix it in an easy way. thanks!
import { Router } from 'express';
import { getUser } from '../controllers/userController';
import { validateJWT } from '../middlewares/validateJWT';
const router = Router();
router.get('/user', validateJWT, getUser);
export default router;
const getUser = async (req: Request, res: Response, next:
NextFunction): Promise<any> => {
try {
const { id } = req.params;
if (!id) {
let response = formatErrorResponse({ error: true, statusCode: 400, errorMessage: 'Missing id in params' });
return res.status(400).json(response);
}
let user = await User.findById({_id: id});
let objRes = { error: false, statusCode: 200, message: 'User was found', data: user };
return res.status(200).json(objRes);
} catch (error) {
console.log('error in controller /user', error)
next(error);
}
}
export {
getUser
}
const validateJWT = async (req: Request, res: Response, next: NextFunction): Promise<any> => {
const token = req.header('x-token');
console.log(req)
if (!token) {
const err = formatErrorResponse({ error: true, statusCode: 401, errorMessage: 'Missing header x-token' });
return res.status(401).json(err);
}
try {
await verifyToken(token);
next();
} catch (error) {
const err = formatErrorResponse({error: true, statusCode: 401, errorMessage: 'Invalid token, athorization denied', errorContent: error});
return res.status(400).json(err);
}
}
You need to change your implementation of the function to work like this:
router.get('/user', (req, res) => {
validateJWT(req, res);
getUser(req, res);
});
Since express.Router instances expect the route as a string for the first parameter which you have as "/user". And a second parameter which is a callback.
Inside that callback you can call your functions.
Assuming you need validateJWT to finish running before getUser you could do something like this:
validateJWT(...).then((...) => getUser(...));
I suggest something like the below.
router.use(validateJWT)
router.get('/users/:id', (req, res, next) => {
getUser(req.params.id)
.then(res.json)
.catch(next);
});
In the docs, I could see an async middleware example http://expressjs.com/en/guide/writing-middleware.html#mw-fig.
I could not find an async handler example. I recall that back in the day, express didn't support async handler. Hence, I used .then inside the handler. You need to double-check if you can use them nowadays.
In the express docs is also an example like the below. It makes working with async handler more convenient in some regard. As you can wrap all your async handler with this wrapper.
const wrap = fn => (...args) => fn(...args).catch(args[2]);
router.get('/users/:id', wrap(getUser));

Node.js MSSQL package query function not returning data

I'm writing my first Node.js REST API. I'm using MS SQL Server as my database. I am using the npm package mssql to work with my SQL server DB.
I took the code below directly from the mssql docs example page. I simply wrapped it into a function.
function getServices() {
sql
.connect(config)
.then((pool) => {
// Query
return pool
.request()
.input("SID", sql.Int, 1)
.query(
"select * from [dbo].[Services] where ServiceId = #SID"
);
})
.then((result) => {
//console.dir(result); //this has data.
return result;
})
.catch((err) => {
console.log(err);
return err;
});
}
The above code works just fine and gets the data from my DB. The issue happens when I try to make this code into a function that I can use on my express route, as shown below.
router.get("/", (req, res, next) => {
const data = getServices();
console.log("data: ", data); //this comes back as undefined
res.status(200).json({
message: "Handling GET request from /services router.",
});
});
From reading the docs and all the others posts on stackoverflow. I am using the .then() promise mechanism, so why is my "result" not getting back to the function on my express route? The "data" const on the express route is always undefined. What am I missing here?
Rule of thump: calling a function const data = getServices(); means that this function doesn't contain any asynchronous work like async/await/.then/.catch/Promise.
Once a function containes one of those, it should always be called with one of the above.
SO #1 you must change
router.get("/", async (req, res, next) => {
const data = await getServices();
console.log("data: ", data); //this comes back as undefined
res.status(200).json({
message: "Handling GET request from /services router.",
});
});
Then the function itself
function getServices() {
try {
return sql
.connect(config)
.then((pool) => {
// Query
return pool
.request()
.input("SID", sql.Int, 1)
.query(
"select * from [dbo].[Services] where ServiceId = #SID"
);
})
.then((result) => {
//console.dir(result); //this has data.
return result;
})
} catch(ex){
return ex;
}
}
I kept digging through some more SO posts and found my working solution. Updated, working code is posted below.
router.get("/", (req, res, next) => {
getServices().then((result) => {
res.status(200).json(result.recordset);
});
});
function getServices() {
return new Promise((resolve) => {
sql
.connect(config)
.then((pool) => {
// Query
return pool
.request()
.input("SID", sql.Int, 1)
.query(
"select * from [dbo].[Services] where ServiceId = #SID"
);
})
.then((result) => {
//console.dir(result);
resolve(result);
})
.catch((err) => {
console.log(err);
resolve(err);
});
});
}

Unable to fetch the available data with the request params id in node.js

I have a following get request on the server:
payments.get('/:id', (req, res) => {
Payment.findAll({
where: {
requestID: req.params.id
}
})
.then(res => {
res.status(200).json({status: "Ok", res})
})
.catch(err => {
res.status(404).json({status: false});
// res.send(err);
})
})
When I test to get all the payment details from the given request params id in postman then I failed to fetch the available data from the database and it always runs to the catch() function as shown in the figure below:
When I hit the Send button in postman, my nodejs console will execute the following query function:
When I tested this query in the XAMPP Server, then it shows the data.
SELECT `payment_id`, `stud_uuid` AS `studID`, `request_id` AS `requestID`, `request_date` AS `requestDate`, `amount`, `ins_uuid` AS `insID`, `status` FROM `tbl_payment` AS `tbl_payment` WHERE `tbl_payment`.`request_id` = 'd690ae99-c6bf-4568-ad2b-980bfb4696e8'
The data is as follows:
What I am missing in the nodejs function that I am unable to show the available data??
Why my get request is always going to the catch() statement..
Any help is appreciated.
The problem is here:
payments.get('/:id', (req, res) => {
Payment.findAll({
where: {
requestID: req.params.id
}
})
.then(res => {
// −−−−−−−^^^
res.status(200).json({status: "Ok", res})
})
.catch(err => {
res.status(404).json({status: false});
// res.send(err);
})
})
You're shadowing the res you received in the get callback with the result of the promise. Rename it:
payments.get('/:id', (req, res) => {
Payment.findAll({
where: {
requestID: req.params.id
}
})
.then(result => {
// −−−−−−−^^^^^^
res.status(200).json({status: "Ok", res: result})
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^
})
.catch(err => {
res.status(404).json({status: false});
// res.send(err);
})
})
Problem is res being used here promise resolution object of Payment.findAll, instead use some other variable so that res doesn't get overridden.
payments.get('/:id', (req, res) => {
Payment.findAll({
where: {
requestID: req.params.id
}
})
.then(**done** => {
res.status(200).json({status: "Ok", **done**})
})
.catch(err => {
res.status(404).json({status: false});
// res.send(err);
})
})

Translating nested asynchronous functions to node.js Async function

So I'm working on a movie website to practice my node.js/angular skills. Recently I noticed that I have a lot of nested asynchronous functions on my server side. Many people suggested using the async.js module for this.
Below there are two different versions of the same server side objective. One is the original nested async functions and the other one is what I came up with using Async.js. Could someone see if I did it the right way, used the right method in this case (waterfall) and perhaps give me some corrections that could improve my code? It works, but maybe you guys can see some improvements? For example: Is there an alternative to passing the variables to the arguments every time ( like I did with reviewId e.g)
Extra question:
What are the most used methods of the async.js module? Cause there are like hundreds and I would just like to learn the most important ones.
This is what I came up with when I tried to translate the nested asynchronous functions below
router.delete('/deleteReview/:reviewId', function(req, res, next) {
async.waterfall([
function (callback) {
var reviewId = req.params.reviewId;
Review.find({'_id': reviewId})
.populate('user')
.populate('movie')
.exec(function(err, review){
callback(null, review, reviewId)
});
},
function (review, reviewId, callback) {
var index = review[0].user.reviews.indexOf(reviewId);
review[0].user.reviews.splice(index, 1);
review[0].user.save(function(err, user){
callback(null, user, reviewId, review)
})
},
function (user, reviewId, review, callback) {
var index = review[0].movie.reviews.indexOf(reviewId);
review[0].movie.reviews.splice(index, 1);
review[0].movie.save(function(err, movie){
callback(null, movie, index)
});
}
], function (err, result, index) {
if (err) {
res.status(500).json({
message: "An error occurred",
obj: err
})
}
res.status(200).json({
message: "successful",
obj: index
})
});
});
the original nested asynchronous functions
router.delete('/deleteReview/:reviewId', function(req, res, next){
var reviewId = req.params.reviewId;
Review.find({'_id': reviewId})
.populate('user')
.populate('movie')
.exec(function(err, review){
if (err) {
res.status(500).json({
message: "An error occurred",
obj: err
})
}
var index = review[0].user.reviews.indexOf(reviewId);
review[0].user.reviews.splice(index, 1);
review[0].user.save(function(err, user){
if (err) {
res.status(500).json({
message: "An error occurred",
obj: err
})
}
var index = review[0].movie.reviews.indexOf(reviewId);
review[0].movie.reviews.splice(index, 1);
review[0].movie.save(function(err, movie){
if (err) {
res.status(500).json({
message: "An error occurred",
obj: err
})
}
res.status(200).json({
message: "successful",
obj: index
})
})
});
})
});

Async lookup without results hangs app

I am attempting to use mongoose with async, everything works fine for the most part...however when I perform a lookup that returns no results my application seems to hang and eventually timeout.
Here is some example controller code that does a simple lookup by id using mongoose and async:
module.exports.find = function(req, res) {
async.waterfall([
function(next) {
SomeModel.findById(req.params.id, next);
},
function(someModel, next) {
if (!SomeModel) {
res.status(404).json({
success: false,
message: 'SomeModel not found'
});
} else {
res.json(SomeModel);
}
}
]);
};
If a record is found everything comes back just fine, however for a nonexistent id it seems that the second async step is never called and eventually the whole request times out.
So what am I doing wrong here? How do I get the 'findById' method to call 'next' even if a record isn't found?
Mongoose is throwing an error, and you aren't catching it. The other thing I should mention is that you should be doing your response handling in the final callback (which you have not defined).
Try something like this:
module.exports.find = function(req, res) {
async.waterfall([
function(next) {
SomeModel.findById(req.params.id, next);
}
], function(err, SomeModel){
// this is the final callback
if (err) {
// put error handling here
console.log(err)
}
if (!SomeModel) {
res.status(404).json({
success: false,
message: 'SomeModel not found'
});
} else {
res.json(SomeModel);
}
});
};
Alternatively, you could just simplify it to not use waterfall:
module.exports.find = function(req, res) {
SomeModel.findById(req.params.id, function(err, SomeModel){
if (err) {
// put error handling here
console.log(err)
}
if (!SomeModel) {
res.status(404).json({
success: false,
message: 'SomeModel not found'
});
} else {
res.json(SomeModel);
}
});
};

Categories