Express - Validation express-validator does not show errors - javascript

I'm sure this is not a problem instead I'm not using it the right way.
I want to validate user request body and according to documentation, all work is done in the route file. And I divided my work into routes and controllers files. I passed a validation middleware and it needs to be validated but it's not. Something wrong in between.
My route is:
router.post(
'/signup',
userController.validateRequest,
userController.hashPassword,
userController.signup
);
Instead of this, if I did this way:
router.post(
'/signup',
body('email').isEmail().withMessage('Please enter a valid email'),
body('password')
.equals('confirmPassword')
.withMessage('Passwords do not match'),
userController.hashPassword,
userController.signup
);
That will work But this will makes code messy if several validation requests are there.
I just want to know what is the correct way, if I separate requests and validation middleware OR do I have to pass validation in route file ONLY. Please correct me if I'm doing it the wrong way.
My validation middleware is:
exports.validateRequest = (req, res, next) => {
if (req.body.email) {
body('email').isEmail().withMessage('Please enter a valid email');
}
if (req.body.password_confirm) {
body('password')
.equals('confirmPassword')
.withMessage('Passwords do not match');
}
next();
};
And my controller where all error messages are displaying
exports.signup = async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
............

Well i prefer Joi the most powerful validator
async createAccountSchema(req, res, next) {
// create schema object
const schema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string()
.min(8)
.required(),
confirmPassword: Joi.string()
.valid(Joi.ref('password'))
.required()
.label('Passwords')
.messages({ 'any.only': '{{#label}} are not the same' }),
})
// schema options
const options = {
abortEarly: false, // include all errors
allowUnknown: true, // ignore unknown props
stripUnknown: true, // remove unknown props
}
// validate request body against schema
const result = schema.validate(req.body, options)
if (result.error) {
return res.status(400).json(result.error.details)
} else {
result.value.email = result.value.email.trim().toLowerCase()
result.value.password = result.value.password
req.body = result.value
next()
}
}
Thats an example for the validator. You pass req.body to the validator and it do the job.
After validating you set your req.body to the validation result and use next()
app.use("/", createAccountSchema, userController.signup)

Related

Creating an express middleware to send emails

I've been trying to make an express middleware that sends an email using Nodemailer after the previous middleware finishes. I've come up with a few different designs, but ultimately each different version has it's drawback.
Ultimately, I would like the middleware to have a response from the previous middleware. If it is a success, then send a success email, otherwise, send an error email.
I came up with a dual design where one variation pushes to an error middleware, and a success leads to the next middleware. This contains some slight issues of sending multiple headers, specifically on an the second middleware erroring. I could say, if the mail errors out, do nothing. But that doesn't seem right. If anyone has any suggestions on a good design, that would be great.
From what you described, I would suggest not to create different middleware for that, but to just create one generic email function that would handle different type of messages. Then, just use that function in the first middleware and pass different parameters based on use case (success/error).
email-controller.js
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD,
},
});
exports.send_email_message = (send_to, subject, message) => {
return new Promise((resolve, reject) => {
const email_message = {
from: { name: process.env.EMAIL_FRIENDLY_NAME },
to: send_to,
subject: subject,
text: message
};
transporter.sendMail(email_message).then(() => {
resolve(true);
}).catch((error) => {
reject(false);
});
})
}
custom-router.js
const { send_email_message } = require('./email-controller');
const express = require('express');
const router = express.Router();
router.get('/custom-middleware', async (req, res, next) => {
try {
// You can calculate "success" variable based on your custom logic
if(success){
await send_email_message('example#gmail.com', 'Success', 'This is body of success message.');
return res.status(200).json({ success: true });
} else {
await send_email_message('example#gmail.com', 'Error', 'This is body of error message.');
return res.status(400).json({ success: false });
}
} catch(error) {
return res.status(400).json({ success: false });
}
});
module.exports = router;

How does express-validator prevent this function call from happening?

This is straight out of the express-validator documentation. I noticed that when these functions are passed as middleware, they include arguments and parenthesis, in which case they should be called at runtime right?
// ...rest of the initial code omitted for simplicity.
const { body, validationResult } = require('express-validator');
app.post(
'/user',
// username must be an email
body('username').isEmail(),
// password must be at least 5 chars long
body('password').isLength({ min: 5 }),
(req, res) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
User.create({
username: req.body.username,
password: req.body.password,
}).then(user => res.json(user));
},
);
I jumped into the source code to try and figure out how they are preventing the function calls, but it is a little over my head. The reason I wanted to learn about this was I was interested in creating a middleware that worked in a similar fashion, where arguments could be passed without actually calling the function at runtime.
I'm not going to reverse engineer some specific code, but will explain how to achieve this in general.
See the documentation for middleware for reference.
An endpoint is a function that takes two arguments. The request and the response. They are typically named req and res.
Middleware takes three arguments. The third is next which is called to pass control to the next function.
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
Now, middleware doesn't have to pass control to the next function. It can just respond.
const middleware = (req, res, next) => {
if (typeof req.body?.username === 'undefined') {
// No username was provided
res.send("Error: No username was provided");
} else {
next();
}
}
Now you might want this to be reusable for arguments other than username, so you can write a factory function which returns the middleware function.
const createMiddleware = (propertyName) => {
const middleware = (req, res, next) => {
if (typeof req.body?.[propertyName] === 'undefined') {
// No value was provided for the propertyName
res.send(`Error: No ${propertyName} was provided`);
} else {
next();
}
}
return middleware;
}
And then use it:
app.use( createMiddleware('username') );
app.use( createMiddleware('password') );

Express POST API Route not receiving a Supertest request

I am trying to test an Express API POST Route that uses Express Validator for check:
usersRouter.post(
'/',
[
check('name', 'Please enter a name.').not().isEmpty(),
check('email', 'Please enter a valid email.').isEmail(),
check(
'password',
'Please enter a password of 6 characters or more.'
).isLength({ min: 6 }),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log('errors: ', errors);
return res.status(400).json({ errors: errors.array() });
}
const { name, email, password } = req.body;
try {
//...
}
catch {
//...
}
}
);
This API route expects to receive a request consisting of a body that contains the fields, name, email, and password:
const { name, email, password } = req.body
In order to test this route, I have a test file using supertest and jest:
const mongoose = require('mongoose');
const supertest = require('supertest');
const app = require('../app');
const testApi = supertest(app);
const User = require('../models/User');
test('a token is returned', async () => {
// Create a new test user for the HTTP request.
const newTestUser = {
name: 'bob',
email: 'test#test.com',
password: 'newtestpw',
};
const { name, email, password } = newTestUser;
const body = await JSON.stringify({ name, email, password });
// Execute the test.
const config = {
headers: {
'Content-Type': 'application/json',
},
};
let result = await testApi.post('/api/users', body, config);
expect(result.status).toBe(200);
expect(result.headers).toHaveProperty('token');
});
afterAll(async () => {
await mongoose.connection.close();
});
When I execute this test, each check in the POST API route fails. The following errors is returned:
errors: Result {
formatter: [Function: formatter],
errors:
[ { value: undefined,
msg: 'Please enter a name.',
param: 'name',
location: 'body' },
{ value: undefined,
msg: 'Please enter a valid email.',
param: 'email',
location: 'body' },
{ value: undefined,
msg: 'Please enter a password of 6 characters or more.',
param: 'password',
location: 'body' } ] }
Why is the API route not receiving the request I'm sending using Supertest?
Well, it seems that you're not sending your values right.
Look closely at where you send your name, email and password, and how you send them. You can try going to your route and console.log the values it's getting.
And look in how the api.post function actually works. I suggest looking on the Supertest github page and Superagent docs
Just in case you want to try to solve the problem on your own, I hid the solution in the spoiler. But in short, first:
You don't need to stringify your body. You should send it as a usual JavaScript object. Also, you don't need to await the JSON.stringify, as it doesn't return a promise, it's synchronous
Second:
The api.post function only takes the URL as its argument. To send any data you want along with the request you need to chain .send(data) after the .post
Third:
The headers are also set by chaining the .set('Header', 'value') method before or after the .send
So in the end, your request should look something like this.
testApi
.post(url)
.set('Content-Type', 'application/json')
.send(newTestUser)

Why doesn't my custom callback in Passport JS work?

I'm trying to get the user to be able to sign up to my website, and store the credentials on mongodb.
This is my auth.js file, where the route is defined:
router.post('/signup', (req,res,next) => {
Passport.authenticate('local-signup', err => {
if (err) {
if (err.name === "MongoError" && err.code === 11000) {
res.status(409).json({
success: false,
message: "Unsuccessful",
errors: {
email: "This email is already taken."
}
});
}
res.status(400).json({
success: false,
message: "Unsuccessful",
errors: {
unknown: "Could not process for some reason. Contact admin."
}
});
}
res.status(200).json({
success: true,
message: "Successful",
errors: {}
});
}) (res, req, next);
}
That last bracket got a bit messed up but believe me, it's not a syntax error.
This snippet is where I have defined the passport strategy:
require ('../Models/Users')
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/onlinestore');
const User = mongoose.model('User');
const PassportLocalStrategy = require('passport-local').Strategy;
const Passport = require('passport');
module.exports = Passport.use(new PassportLocalStrategy({
usernameField: 'email',
passwordField: 'password',
session: false,
passReqToCallback: true,
}, (email, password, done) => {
let user = new User();
user.email = email.trim();
user.password = password.trim();
user.save(err => {
if (err) {
console.log(err);
return done(err);
} else {
console.log("Success");
return done(null);
}
});
}
));
The route is able to get the user inputted password and user. When I click submit, literally nothing happens; the server doesn't console anything nor does the client. After a bit of debugging I think the issue is due to the fact that Passport.Authenticate is not being called but I'm not sure why. I can post other code snippets if necessary, thanks!
This is OP. I was able to find the solution.
1) In my auth.js file,
Passport.authenticate('local-signup', err => {
replace the 'local-signup' with 'local'
so it becomes:
Passport.authenticate('local', err => {
2) I happened to have multiple Passport strategies and each are in their own file, (one for login and one for signup). To use the correct "local" I have to import the correct one in my auth.js file. Easy enough, it's
const passport = require('../Passport/local-signup');
For login, it would be a different file.

Access request body in check function of express-validator v4

I just started using express.js with express-validator to validate some input data and I have problems accessing the request body in the new check API that was introduced in version 4.0.0.
In older versions, you simply added express-validator as middleware in your app.js somewhere after body-parser:
// ./app.js
const bodyParser = require("body-parser");
const expressValidator = require("express-validator");
const index = require("./routes/index");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(expressValidator());
Then in my index route, I could check the fields in the final callback function of the post method.
// ./routes/index.js
const express = require("express");
const router = express.Router();
router.post("/submit", (req, res, next) => {
// check email
req.check('email','Invalid email address').isEmail()
// check if password is equal to password confirmation
req.check('password', 'Invalid password')
/* Access request body to compare password
field with password confirmation field */
.equals(req.body.confirmPassword)
// get errors
const errors = req.validationErrors();
// do stuff
});
Like in this example, I could easily check whether the values of my password field and the password confirmation field of my form are equal. However, since version 4, they have a new API which requires you to load the express-validator directly in your router file and pass the check functions as array of functions before the final callback in the post method, like this:
// ./routes/index.js
const express = require("express");
const router = express.Router();
const { check, validationResult } = require("express-validator/check");
router.post(
"/submit",
[
// Check validity
check("email", "Invalid email").isEmail(),
// Does not work since req is not defined
check("password", "invalid password").isLength({ min: 4 })
.equals(req.body.confirmPassword) // throws an error
],
(req, res, next) => {
// return validation results
const errors = validationResult(req);
// do stuff
});
This doesn't work since req is not defined. So my quetsion is: how can I access the request object in a check() chain to compare two different fields with the new express-validator API? Thanks very much in advance!
After fiddling around for a while, I found a way to achieve this by using custom validators. The validator function passed to the custom method accepts an object containing the request body:
router.post(
"/submit",
[
// Check validity
check("email", "Invalid email").isEmail(),
check("password", "invalid password")
.isLength({ min: 4 })
.custom((value,{req, loc, path}) => {
if (value !== req.body.confirmPassword) {
// trow error if passwords do not match
throw new Error("Passwords don't match");
} else {
return value;
}
})
],
(req, res, next) => {
// return validation results
const errors = validationResult(req);
// do stuff
});

Categories

Resources