Let's say I have a route:
// routes.js
router.post(
'/someroute',
[
body('email')
.isString()
// more validation functions here
],
controller.doSomthing);
and I check for errors using validationResult(req):
// controller.js
exports.doSomething = (req, res, next) => {
const errors = validationResult(req);
if(!errors.isEmpty()) {
console.log(errors);
const error = new Error('Validation Error');
error.statusCode = 422;
throw error;
}
};
Obviously I don't need to test the validationResult function, but what about the validity of the rules I'm using with it?
Is there any way to extract something like this:
[
body('email')
.isString(),
// more validation
body('name')
.isLength({ min:4, max:50 })
]
into a function or class I can use as middleware in the router.post('/someroute',...) function above, and also import that file for separate testing?
Would this have any advantages over using something like chai-http?
Apologies if this is obvious, I've tried looking at the express-validator docs with not much luck/understanding. I'm pretty new to express and testing.
Related
When I am entering localhost://5000/products/
Its showing me all the json objects
But when I am entering localhost://5000/products/(some random id's) It must show me the 404 error with my custom message but its not showing me that.
This is my code:
import express from 'express'
import asyncHandler from 'express-async-handler'
const router = express.Router()
import Product from '../models/productModel.js'
// #Desc Fetch all products
// #Desc GET /api/products
// #access Public
router.get(
'/',
asyncHandler(async (req, res) =>{
const products = await Product.find({})
res.json(products)
}))
// #Desc Fetch single products
// #Desc GET /api/products:id
// #access Public
router.get(
'/:id',
asyncHandler(async (req, res) =>{
const product = await Product.findById(req.params.id)
if(product) {
res.json(product)
} else {
res.status(404).json({ message: 'Product not found' })
}
}))
export default router
Your question is somewhat lacking in details, but I do believe I know what's happening. Your idea is that if you pass a non-existent id to the route and mongo does not find it, it will return a 404 - Not Found. Solid logic.
The reason you're not seeing that 404 is, most likely, because you are calling the route with something like /products/dflhdshfd. The problem with that is that findById only accepts strings that can be cast to an ObjectId, which, of course dflhdshfd cannot be. Hence when you call your route with /products/dflhdshfd, your app bombs and throws this error:
CastError: Cast to ObjectId failed for value "dflhdshfd" at path "_id" for model "product"
And your app does not return anything because you don't handle that error.
As a test, try calling the route like this: /products/601977b2d2e3cf756b85bc61 and you will see your 404, because 601977b2d2e3cf756b85bc61 can be cast to a valid ObjectId.
But more generally, you need to catch errors coming from Product.findById(req.params.id) by doing something like this:
const product = await Product.findById(req.params.id).catch(e => false);
if(product) {
res.json(product)
} else {
res.status(404).json({ message: 'Product not found' })
}
This way when your findById throws an error, it will return false to the product variable and your app will return a 404. (This is not a great way to handle errors and is simply meant to illustrate the point)
Added
Properly handling errors can be a daunting task in a large application, so I will address your specific case only while encouraging you to go over this article for a more complete picture.
The way I would handle this specific error is I would first create a small function to route all the caught errors to. Something simple like this:
const errorHandler = (err, res) => { //res is the response object
console.log(err);
res.status(400).json({msg: "Error has occurred"})
}
Then I would rewrite your code like this:
router.get(
'/:id',
(req, res) => {
Product.findById(req.params.id)
.then(product => {
if(product) {
res.json(product)
} else {
res.status(404).json({message: 'Product not found' })
}
})
.catch(e => errorHandler(e, res))
})
Whenever findById throws an error, it simply sends it over to errorHandler to deal with, while errorHandler dumps it to the console for you to troubleshoot and also send a response back to the user.
And that can be the template you adopt for ALL your queries (don't forget to always catch db query errors). Later you can, for example, add a logging feature to errorHandler that dumps errors into a file for you to later look at.
So this would be the basic approach I would take here.
I'm testing a nodejs app written using express. For the unit testing I'm using chai and sinon. I have the following route in my API that I would like to test.
In my test, I'm simulating the get request with the following code:
chai.request(app)
.get('/downloads')
.send({ wmauth: {
_identity: {
cn: "username",
}
} })
.end((err, res) => {
res.status.should.be.equal(200);
res.body.should.be.a('object');
res.body.should.have.property('Items', []);
AWS.restore('DynamoDB.DocumentClient');
done();
However, I'm always getting the error "Cannot read property '_identity' of undefined". Because the object "wmauth" is not sent in the request, so it is undefined. I have tried to use the send method to try to include it in the request, but no luck. I guess I need to mock it somehow and send it into the request but have no idea how to do it. Could someone help me with this?
Below the method to test:
app.get('/downloads', async (req, res) => {
const created_by_cn = req.wmauth['_identity'].cn;
if(!created_by_cn) {
return res.status(400).json({
error: 'Mandatory parameters: created_by_cn',
});
}
try {
const data = await downloadService.getDownloads(created_by_cn);
return res.status(200).json(data);
}
catch(error){
res.status(500).json({error: error.message});
}
});
THanks
I guess you forgot to use req.body as in:
const created_by_cn = req.body.wmauth['_identity'].cn;
Hope can solve your issue
Since chai-http use superagent, so according to its doc, you need to use query() in order to pass query parameter in get request:
chai.request(app)
.get('/downloads')
.query({ wmauth: {_identity: {cn: "username"}}})
.end((err, res) => { ... });
Then in the express route you can find the parameters in req.query:
app.get('/downloads', function (req, res) {
const created_by_cn = req.query.wmauth._identity.cn;
...
})
I have an Express application with a router, here is the example of the router:
const router = require('express-promise-router')();
const users = require('./users');
const validate = require('./validate');
router.get('/users', users.list);
router.get('/users/:id', users.get);
// other routes here
module.exports = router;
Now I want to add a middleware that validates each query, like that (this is not the working example, it's just to show the idea of what I want to accomplish):
const schemas = {
'/users': 'some validation schema',
'/users/:id': 'another validation'
}
module.exports = (req, res, next) => {
const url = req.originalUrl; // This is where I'm stuck.
if (!schemas[url]) {
// throw new error that validation failed
}
// validating somehow
if (!req.validate(schemas[url])) {
// throw new error that validation failed
}
return next();
}
And for this, I need to get the middlelware mount folder (like '/users/:id' for '/users/557'). I've tried to use req.originalUrl, but it returns the full URL path instead of the mount folder.
How can I achieve this? And if there's no way, how can I write my validation middleware another way to make it work?
Inside req.route you will get the path of API.
Check this screenshot
I'm having trouble getting centralized error handling set up in my restify app. I'd like to trap certain Mongo errors, such as "E11000 duplicate key error" and then map them to a restify ConflictError.
If I just let the error bubble up from my Mongo call in a route, the client gets a 500 error.
I figured I should trap InternalServerError, but the below handler never gets called:
app.on('InternalServerError', function (req, res, err, cb) {
console.log('++++++++++++++++', err);
return cb(err);
});
I thought I could just use the express approach:
app.use(function (err, req, res, next){...
But restify handlers don't seem to take an error argument. I'm stumped after searching all the usual places. It seems my first approach should have just worked.
This might work for you. Set up a bunyan logger in your app.js file…
var bunyan = require('bunyan');
var log = new bunyan({
name: 'my_api',
streams: [
{
path: './error.log',
level: 'warn'
}
],
serializers: {req: restify.bunyan.serializers.req},
src: false
});
var server = restify.createServer({
log: log
});
Then in your controller do something like this….
var restify = require('restify');
try {
Model.findAll().then(function(vals){
res.send(vals);
next();
});
}
catch(e) {
req.log.error({req_id: req.id()}, 'Error attempting find.');
res.send(409, new restify.ConflictError("Problem executing search."));
next();
}
I have registration form and posting the values to register function where it will validate form values with node-validator.
But, when I tried with node-validator, it keeps getting error as
TypeError: Object # has no method 'onValidationError'. The validator already installed in my app.
In app.js
app.post('/register', test.register);
In test.js
var check = require('validator').check,
sanitize = require('validator').sanitize;
exports.register = function(req, res){
var errors = [];
req.onValidationError(function(msg) {
//res.render('signup', { error: msg });
errors.push(msg);
});
req.check(req.body.email, 'Please enter a valid email').len(6,64).isEmail();
req.check(req.body.username, "Username can't be empty!").isNull();
if (errors.length)
return res.render(
'register', { regErr: errors.join('\n') }
);
}
How to correct this error?
You are using node-validator . You should be using express-validator for onValidationError
From node-validator page you can see it:
If you are using the express.js framework you can use the
express-validator middleware to seamlessly integrate node-validator.
Example http://localhost:8080/?zip=12345&foo=1&textarea=large_string
get('/', function (req, res) {
req.onValidationError(function (msg) {
//Redirect the user with error 'msg'
});
Additional Note:
The order of app.use is also important. Try to move app.use(expressValidator); up of app.use(app.router);. You can view the order of app.use in https://github.com/justin-john/node-user-management/blob/master/app.js#L17-L18.