Efficient way to handle error cases in REST Apis - node.js

I am writing a REST API in node.js. I expect at max 5 parameters to arrive in my GET Request. If there are unidentified parameters I wish to send 400 Bad Request.
Currently I am handling it in the following way:
server.route({
method: "GET",
path : "/test",
handler : function (request, reply) {
if (request.query.a || request.query.b || request.query.c || request.query.d || request.query.e)
{
// do some processing
}
else {
reply("No valid parameters").code(400);
}
}
});
Right now this does not handle the case if there are some valid and some invalid cases. I can solve that by using more if conditions. But I was just wondering if there is a standard or more efficient method which is used by developers

Hapi has built-in validation support. You can use Joi to validate the query:
var Joi = require('joi');
...
server.route({
method: 'GET',
path: '/test',
config: {
validate: {
query: {
a: Joi.string(),
b: Joi.string(),
c: Joi.string(),
d: Joi.string(),
e: Joi.string()
}
}
},
handler: function (request, reply) {
return reply('ok');
}
});
If you send a request to /test?g=foo, then you get the following response:
{ statusCode: 400,
error: 'Bad Request',
message: '"g" is not allowed',
validation: { source: 'query', keys: [ 'g' ] } }

You can manually do this, but you are better off using a REST framework like mongoose or loopback (strongloop) to do the validation and error handling and other boilerplate code like model binding.

Perhaps you should consider the use of a framework, like Express, to use a middleware which is simply code you can reuse in different routes.
A simple pseudo/example looks like this
var app = express();
// a middleware with no mount path; gets executed for every request to the app
app.use(function (req, res, next) {
if (req.query.a && req.query.b) {
return next()
}
else {
return res.status(400).send('Not OK');
}
});
// a route and its handler function (middleware system) which handles GET requests to /user/:id
app.get('/user/:id', function (req, res, next) {
return res.send('OK');
});
// a different route, same middleware
app.get('/customer/:id', function (req, res, next) {
return res.send('OK');
});

Related

node js express - middleware next() is not triggering consecutive fm in mocha test cases

I have configured a middleware (router level) which checks for admin access, and if successful I am logging the information and calling next().
middleware:
const validateAdmin = function (scope) {
return function (req, res, next) {
if (req.admin) {
//log the information and using scope to log
next();
} else {
// send the response with 403
}
};
};
router level usage
router.use('/rule/:name', validateAdmin({ serviceScope: this.#serviceScope }), async (req, res) => {
await this._handleRequest(req, res);
});
working case
when i trigger the req from postman, i can see that middleware is called and after next getting executed, the control is coming to 'await this._handleRequest(req, res);' which is expected.
issue
Test case
it.only('shall get single rule', async function () {
const getSingleReq = {
method: 'GET',
admin: true,
originalUrl: '<some req>/rule/rule_1',
baseUrl: '<some_req>',
query: {},
params: { name: 'rule_1' },
headers: {
//token info
},
body: {}
};
const next = sinon.spy();
await router.use.args[1][1](getSingleReq, res, next);
}));
when await router.use.args[1][1](getSingleReq, res, next); is called, I can see the middleware is triggered but next() is not calling the subsequent middleware. I expect that await this._handleRequest(req, res); is called just like the scenario when triggered from postman.
I hope the issue is clear.
Referred many use cases but everywhere the scenario of just checking if next() is called is only done .

nodeJs express petitions from ajax don't show error's page

I have a configuration to serve a website with an error handler. It works fine with app.post requests, but it does not work with ajax petitions.
Here is my error handling middleware with an example of bad path error that works correctly:
//Bad path error
app.get('*', function(req, res, next){
next('404'); //This works fine
});
//Errors control Middleware
app.use(function (err, req, res, next) {
if (typeof (err) === 'string') {
var lang = require(path.join(__dirname, '_public', 'errors', 'lang', process.env.LANG + '.js'));
return res.render(path.join(__dirname, '_public', 'errors', err), {lang});
} else {
log.error(err);
var lang = require(path.join(__dirname, '_public', 'errors', 'lang', process.env.LANG + '.js'));
return res.render(path.join(__dirname, '_public', 'errors', '500'), {lang});
}
});
when I navigate to a wrong url (some url that isn't defined on my server) it goes correctly to the defined error page (404, that is an .hbs). The problem is that this method doesn't seem to work with ajax petition like the next one:
jQuery.ajax({
type: 'POST', //I have the same problem with 'GET'
url: '/componentName/methodName',
data: {
accessToken: localStorage.token
},
}).then(function success(data) {
//TODO
}, function error(err) {
console.log(err);
})
This piece of code is for server side:
app.post("/componentName/methodName", function (req, res, next) {
var token = req.body.accessToken;
var decodedToken = jwt.decode(token);
var user_id = decodedToken.payload.user_id;
model.getTasks(user_id).then(function (modelData) {
res.send(modelData); //This works fine
}, function () {
next('500'); //This broke the page and doesn't show error page
});
});
What could be the problem for not showing the error from ajax? Is my syntax correct?
new: The problem must be on the ajax success, because if I change the 'post' by 'get' in ajax and the app.get route I still having the same problem, but if I call the method directly from URL (not ajax) it works. Any idea?
new2: If I put this code on success:
jQuery('body').html(data);
it shows the error page after a few seconds. I need to do this automatically (and without those fews seconds) when any error is trhowing from the server, because I don't know if it will be ok or if is an error and the lag seconds is a problem too. Probaly anything on the server could be wrong? and it inject the error page inside the older page, so isn't a solution
model.getTasks(user_id).then(function (modelData) {
res.send(modelData); //This works fine
}).catch(function () {
next('500');
})
Use catch block

Express: express-unless breaking with simple example

https://github.com/jfromaniello/express-unless
Two questions..
Any idea why this does not work?
It appears this library is abandoned. Are there any other alternatives?
handleExpiredTokens.unless({
path: [{
url: '/foo',
methods: ['POST']
}]
});
function handleExpiredTokens(err, req, res, next) {
// stuff
}
handleExpiredTokens.unless = unless;
module.exports = handleExpiredTokens;
handleExpiredTokens runs on every request including POST /foo
getting a protectWithCSRF.unless is not a function

Will emitting request and response handler in Express.js cause problem?

I have a doubt in my design pattern on my Express app,
so i wrap my controller in try and catch, and the catch method is emitting (req, res) handler from controller and later will be handled by a function that send response back to the client.
the code is more or less like this :
const errorExceptionHandler = fn => (req, res, next) => {
fn(req, res, next).catch((err) => {
emitter.emit('onControllerError', {
err: err,
req: req,
res: res,
next: next
})
})
}
the code above emtting req, res, and next, the default parameters that express provided.
emitter.on('onControllerError', params => {
const err = params.err
const req = params.req
const res = params.res
const next = params.next
if (!res.headerSent) {
res.send({
status: 500,
url: process.env.DEBUG ? req.url : undefined,
message: process.env.DEBUG ? err.message : "Something went wrong!"
})
}
})
and above is how the 'onControllerError' event is handled, my concern is, will this cause trouble later if the traffic goes up? or will it send a wrong response to the client?
Increased traffic wouldn't matter here as each request is still handled independently, plus all the necessary data is being passed directly to the event handler.
So no, based on your code I can't think of any reason why it would start to fail.

NODEJS how to connect several express.method() via middleware

I have built , using express() , a variety of methods. for simplicity let's I assume I built 2 POST() functions and I want to be able to use them by themselves and also to concatenate them via middleware for combine usage.
app.post('/create_obj_1' , function (req,res) {
//create Object_type_1
// send Object_type_1 via EXTERNAL API to somewhere
res.json({{ "statusCode": 200, "message": "OK" }
}
app.post('/create_obj_2' , function (req,res) {
//create Object_type_2
// send Object_type_2 via EXTERNAL API to somewhere
res.json({{ "statusCode": 200, "message": "OK" }
}
I want to have a new POST() that can invoke both of the other 2 (but still support stand alone invoking of the original 2
I think it's possible via middleware but I am not sure how - this is how I thought the new POST() should look like -
app.post('/create_obj_all' , function (req,res) {
//I want to invoke the create_obj_1 & create_obj_2 , check all OK, and finish
res.json({{ "statusCode": 200, "message": "OK" }
}
I am not sure how to approach the middleware usage in such case.
On top - how can I connect them to use one each other res? let's say the EXTERNAL API returns some value from obj_1 creation which I want to use in obj_2 post() function..
a Pseudo code of my attempt to use request() inside the middlware_1 -
var middle_1 = function (req, res, next) {
req.middle_1_output = {
statusCode : 404,
message : "fail_1"
}
var options = {
method: 'PUT', url: `EXTERNAL_API`, headers:
{
'cache-control': 'no-cache',
'content-type': 'application/x-www-form-urlencoded',
apikey: `KEY`
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
// CODE THAT DO SOMETHING AND GET INFORMATION
// OLD WAY OF res.send here , to allow using in post.POST() was - res.status(200).send(body);
//res.status(200).send(body);
req.middle_1_output.statusCode = 200;
req.middle_1_output.message = "hello world";
});
next(); // trigger next middleware
}
Given the current example, I don't think you can do it unless you tweak the middlewares for the first two routes a bit:
var middleware1 = function(req, res, next) {
//create Object_type_1
// send Object_type_1 via EXTERNAL API to somewhere
next(); // calling next() triggers the next middleware
};
var middleware2 = function(req, res, next) {
//create Object_type_2
// send Object_type_2 via EXTERNAL API to somewhere
next(); // calling next() triggers the next middleware
};
/**
* This middleware is only used to send success response
*/
var response_success = function(req, res) {
res.json({ "statusCode": 200, "message": "OK" });
}
app.post('/create_obj_1', middleware1, response_success);
app.post('/create_obj_2', middleware2, response_success);
app.post('/create_obj_all', middleware1, middleware2, response_success);
Note that this is a very simplistic solution that I made from your example. The actual implementation will depend on what input each middleware is expecting and what output they generate. Also unlike here, there may also be different middlewares for sending the response.
2nd Part Addressing the second part of your question, if I have got you correctly you want to pass the output from middleware1 to middleware2. You can simply attach the output to the req object before calling next();. Like so:
var middleware1 = function(req, res, next) {
// do something
some_external_api_call(function(error, data) {
if (error) {
// handle the error yourself or call next(error);
} else {
req.middleware1_output = data; // set the output of the external api call into a property of req
next();
}
});
};
var middleware2 = function(req, res, next) {
// check to see if the middleware1_output has been set
// (meaning that the middleware has been called from /create_obj_all )
if (req.middleware1_output) {
// do something with the data
} else {
// handle scenario when /create_obj_2 is called by itself
}
next(); // calling next() triggers the next middleware
};
Notice how you have to account for both scenarios where middleware2 is called from POST /create_obj_all or directly from POST /create_obj_2.
3rd Part You should call next from within the callback. See my above example. This is due to the asynchronous/non-blocking nature of javascript.
function middleware(req, res, next) {
// do something
call_1st_external_api(some_options, function(error, data) {
// executed after call_1st_external_api completes
req.output_of_1st_external_api = data; // store the data of this api call for access from next middleware
next(); // calls the next middleware
// nothing here will be executed as next has already been called
});
// anything here will be executed before call_1st_external_api is completed
next(); // this will call the next middleware before call_1st_external_api completes
}
To handle two external APIs in the same middlewares you have to nest them (or use async or promises):
function middleware(req, res, next) {
// do something
call_1st_external_api(some_options, function(error1, data1) {
// executed after call_1st_external_api completes
req.output_of_1st_external_api = data1; // store the data of this api call for access from next middleware
// executed after call_2nd_external_api completes
call_2nd_external_api(some_options, function(error2, data2) {
req.output_of_2nd_external_api = data2; // store the data of this api call for access from next middleware
next();
});
// anything here will be executed before call_2nd_external_api is completed
});
// anything here will be executed before call_1st_external_api is completed
}
You have to handle all the errors above like I've shown in the 2nd Part which I have not shown in the above example for the sake of simplicity.

Resources