I would like to know how I can move the following code into a separate function (in the same file) and call upon it when either I call the POST or PUT routes to add or update documents.
I'm using https://www.npmjs.org/package/express-validator
The following is currently in my POST route but when I'm updating a record the title will still need to be validated.
app.post('/docs', auth, function (req, res) {
req.checkBody('title', 'Title is required').notEmpty();
var errors = req.validationErrors();
if(errors){
res.json(400, { errors: errors });
return;
}
//go ahead and save the document
});
I've tried making my own function but I'm not sure where to put the var errors = req.validationErrors(); or whether it's bad practice to return 400 errors from a separate function.
Any help/code much appreciated.
The body of the middleware function is almost identical to the code you are using right now, only with two notable differences:
The function ensures that the req.method is either POST or PUT.
The next() function is called when validation passes. This will trigger the next middleware function in the chain, or the route handler.
app.use('/docs', function(req, res, next) {
if (req.method == 'POST' || req.method == 'PUT') {
req.checkBody('title', 'Title is required').notEmpty();
var errors = req.validationErrors();
if (errors) {
res.json(400, { errors: errors });
return;
}
}
next();
});
app.post('/docs', auth, function (req, res) {
// go ahead and save the document
});
Related
Is there any middleware to apply for controller when post request is triggered without payload (empty body)? Instead of defining on every post request, and checking has it body or not, would be awesome to have some sort of middleware to apply that automatically catches empty body and sends back 400 error for user with default json message. :)
This is quite easy to make yourself:
app.use((req, res, next) => {
if (Object.keys(req.body || {}).length === 0) {
return res.status(400).json({ message : 'no payload' });
}
return next();
});
Or use a more elaborate validation suite like joi-express.
If you only need to validate empty payload use below middleware function
var requestPayloadEmpty = (req, res, next) =>{
if (Object.prototype.toString.call(value) === '[object Object]' && JSON.stringify(value) === '{}') {
return res.status(400).json({ message : 'No payload' });
}
return next();
}
app.use(requestPayloadEmpty)
If you need more validation than this use express-validator or joi package to validate other stuff
am trying to have a router.get inside router.post, I need to compare the information provided by the user to the one in the database and then post it if it does not exists. The problem is the router.get is never reached. No errors, and postman keeps "sending request" with no end. Is it possible to have a router.get inside router.post?, if yes how?, If no, how do I get info from router.get to pass to router.post? I need to run the api from ..../new, and do all the work from there. Thanks in advance
//register
router.post('/new', (req, res, next)=>{
console.log("jumped out");
var user_id, password0, password1;
user_id = req.body.user_id;
password0 = req.body.password0;
password1 = req.body.password1;
console.log(password1);
//retrieving usernames to check if it exists
router.get('/accounts', (req, res, next)=>{
console.log("in here");
detail.find(function(err, accounts){
//looping through usernames
for (var i=0; i<accounts.length; i++){
if (accounts[i].user_id === user_id){
res.json({msg: 'Username taken'});
}
else if(i == (accounts.length-1)){
if (password0 === password1){
let newAccount = new account({
user_id: this.user_id,
password: this.password0
});
newAccount.save((err, account)=>{
if(err){
res.json({msg: 'failed to create account'});
}
else{
res.json({msg: 'Account created successfully'});
}
});
}
else if (password0 !== password1){
res.json({msg: 'Password mismatch'});
}
}
}
//res.json(accounts);
});
});
});
I think the issue here is that you have a misunderstanding about what router.get is doing. It sounds like you want to accept a POST request from the user, then make a get request to check if the data already exists, and if not, then update your database.
If this is the case, the inner GET should actually use something like axios to make a request. You can make an axios request like this:
const axios = require('axios');
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
If you are actually trying to accept a second request from the user after the POST, then you should NOT do that. Users should only make one request for one action. You'll run into all kinds of issues down that path.
Turns out you can still retrieve data with only .POST, without having .GET. Removing the line having router.get, fixed the problem.
router.get('/accounts', (req, res, next)=>{
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.
I'm trying to pass data between functions that may change per request. Is there a way to globally store such details?
My Idea was to have an authCheck function that is handled whenver someone access any routes /*. that function will then check if their token is valid. If it is, the program continues, if it isnt, then it just stops there. If the token is valid, I wanted to set a variable for their ID that can be accessible without having to constantly do an SQL query in each function.
You can make a use function that always runs and attach data to req, like this example:
app.use(function (req, res, next) {
// Just an example...
db.query('SELECT * FROM tokens WHERE dat = ?', [req.cookies.token], function (err, result) {
// Check token...
if (err || results.length === 0) {
next(err || 'Invalid token');
} else {
// Set in req obj for other functions to access
req.token = results[0].dat;
next();
}
});
});
app.get('/', function (req, res) {
// Example render template with token attached...
res.render('test', {token: req.token});
});
I have a working node.js / express based server and am using jade for templating. Usually there is no problem but a couple of times every day I get an error message when requsting any page. The error is 'failed to locate view'. I don't know why i get this error since it worked fine just minutes before.
The question however is how I can force a crash on this event, for example:
res.render('index.jade', {info: 'msg'}, function(error, ok) {
if (error)
throw new Error('');
// Proceed with response
};
How would I do this? And how would I proceed with the response?
thank you.
You can add an error handling middleware.
app.use(function handleJadeErrors(err, req, res, next) {
// identify the errors you care about
if (err.message === 'failed to locate view') {
// do something sensible, such as logging and then crashing
// or returning something more useful to the client
} else {
// just pass it on to other error middleware
next(err);
}
});
Try this:
app.use(function (req, res, next) {
fs.exists(__dirname + '/views/' + req.url.substring(1) + '.jade', function (exists) {
if(!exists) {
console.log(err);
return next();
}
res.render(req.url.substring(1), { title: "No Controller", user: req.session.user });
}
});