execute validation middleware after validation checks - javascript

I want to create an Express REST API and want to validate the request params and the request body. If everything is fine I want to call the controller logic.
My validation middleware is
const { validationResult } = require('express-validator/check');
module.exports = (req, res, next) => {
const validationErrors = req.validationResult();
if (!validationErrors.isEmpty()) {
// send a 400
}
next();
}
and I use it within my routes before calling the controller. This is a snippet of my topics.js route file
const validation = require('../middleware/validation.js');
const { check } = require('express-validator/check');
router.get('/', topicsController.getAllTopics);
router.get('/:topicId', [
check('topicId').isUUID()
], validation, topicsController.getTopicById);
router.post('/', authenticationCheck, authorizationCheck, [
check('parentTopicId').isUUID() || check('parentTopicId').isNull(), // check if it's a UUID. If not, check if it's null
!check('name').isEmpty(), // is it not empty?
], validation, topicsController.createTopic);
router.put('/:topicId', authenticationCheck, authorizationCheck, [
check('topicId').isUUID(),
check('parentTopicId').isUUID() || check('parentTopicId').isNull(),
!check('name').isEmpty(),
], validation, topicsController.updateTopic);
router.delete('/:topicId', authenticationCheck, authorizationCheck, [
check('topicId').isUUID()
], validation, topicsController.deleteTopic);
I tried to get into it with the docs
https://express-validator.github.io/docs/#basic-guide
but when launching the API I get this error
Error: Route.post() requires a callback function but got a [object
Boolean]
So it seems I am not able to pass in an array first, then the validation middleware and then the controller.
Is there a way to fix the route file? I don't want to handle the validation logic within my controller file. I think this should be done before.

You need to use oneOf to check for conditional params.
In your routes you use check('parentTopicId').isUUID() || check('parentTopicId').isNull() but that returns a boolean and express needs a middleware. oneOf was build especially for this.
Also you should replace !check with check('name').not().isEmpty().

Related

How can a function return an array of validators and also call next()?

I need a function that takes the request body and conditionally creates the validators that will be used on the request. I figured the best way to do this is by creating middleware but I'm running into a problem with express-validator. For express-validator to work the middleware has to return an array of my validators but if I do this I can't call next() and the request doesn't get handled.
// This will fail because I can't call next
const validatorMiddleware = (req, res, next) => {
const {
isFooRequired,
isBarRequired,
} = req.body;
return concat(
isFooRequired && createValidator('foo'),
isBarRequired && createValidator('bar'),
)
}
With the way I'm going about this I basically need a function that calls next() AND returns the array of validators.
I understand express-validator has a way to conditionally add validation rules but there doesn't seem to be an easy way to do this in bulk. I'd have to do this individually with each validation chain which is a huge pain and kind of gross if you ask me. I have several optional fields which are dependent on a lot of validation and I want to do something like this:
const addressValidators = (service) => [
body(`${service}.address.lineOne`).notEmpty().withMessage('Address cant be empty'),
body(`${service}.address.city`).notEmpty().withMessage('city cant be empty'),
body(`${service}.address.state`).isIn(allowedStates).withMessage('invalid state'),
body(`${service}.address.zipCode`).isPostalCode('US').withMessage('invalid zip code'),
];
In case anyone has this same problem I do, here's the solution I found. With express-validator you can run validations imperatively so I created the middleware you see below. Inside my constructValidators function I use the request body to see which fields are present and create my validation chain accordingly.
const validateMiddleware = async (req, res, next) => {
const validationList = constructValidators(req.body);
await Promise.all(validationList.map((validation) => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
res.status(400).json({ errors: errors.array() });
return null;
};
Remember, 3 hours of troubleshooting could save you 10 minutes of reading documentation!

Parsing Body from request to Class in express

I got doubt, im coming from .net with c# and i want to parse my body request as .net does for my automatically, how can i set a class or an interface as a request body in express, i have found many options but all of them just destruct the body into the properties that they need, i need a way or a method that allows me to get only the properties that i specified in my class.
In .Net it will be something like this.
[HttpGet("someName")
Public IActionResult GetSomething(Myclass instance){
// the instance with only the properties that i specified in my class and not the hole body with useless props that i don’t request
instance.someProperty
Return Ok()
}
ASP.net is actually smart enough to understand that when a class is declared as an argument, it must map from the request POST body to the class.
Nodejs and express do not always come with batteries included.
You need to add a middleware that can read the raw request and get the json object you want. If you are only receiving JSON then you need the JSON middleware. If you expect to have URL encoded posts (for file uploading or html s) then you also need to add the urlencoded middleware
const app: Application = express();
(...)
app.use(express.json());
app.use(express.urlencoded());
At this point, you can declare your route, and express will corectly fill the req.body object with your post data.
interface MyPostBody {
foo: string;
bar: string;
}
app.post("/api/someName", (req, res) => {
const instance = req.body as MyPostBody;
console.log(instance.foo);
console.log(instance.bar);
const result = doSomething(instance);
res.send(result);
});
Please be aware that we are just casting the type here, so if your client sends an object that does not conform to the MyPostBody interface, things will break. You probably need to add some validation to ensure the data conforms to you api contract. You can use some validation library like yup for that. To keep it simple I will do something very basic here.
app.post("/api/someName", (req, res) => {
if(req.body.foo === null || req.body.foo === undefined) {
res.status(400).send("foo is required");
return;
}
if(req.body.bar === null || req.body.bar === undefined) {
res.status(400).send("bar is required");
return;
}
const instance = req.body as MyPostBody;
console.log(instance.foo);
console.log(instance.bar);
const result = doSomething(instance);
res.send(result);
});

Nodejs controller is being messy

I'm new to javascript, node.js (or backend at all). I am trying to create a controller for the login page requests and I am confused about getting data from the MYSQL table and User Authentication and working with JWT package !
In my Controller, I first check if the user input is available in the user table (with a simple stored procedure), then I compare the database password and the user input, after this I want to create a token and with limited time. (I have watched some tutorial videos about JWT and there is no problem with it), my main problem is to figure out how to write a proper controller with this functions?
I have 2 other questions:
1.Is it the right and secure way to get data from MySQL table inside the route? Or should I create a JS class for my controller? (I'm a bit confused and doubtful here)
2.Assuming that comparePassword() returns true, how can I continue coding outside of the db.query callback function scope? Because I have to execute comparePasssword() inside db.query callback
loginController.js :
const { validationResult } = require('express-validator');
const bcrypt = require('bcrypt');
const db = require('../../sqlConnection')
let comparePassword = (dbPass, inputPass) => {
bcrypt.compare(inputPass, dbPass, function(err, result) {
console.log(result)
});
}
// for get request
exports.getController = (req, res) => {
res.send('login')
}
// for post request
exports.postController = (req, res) => {
let errors = validationResult(req)
if(!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() })
}
// find data from MYSQL table
let sql = `CALL findUser(?)`
db.query(sql, [req.body.username], (err, res) => {
if(err) console.log(err)
//console.log(Object.values(JSON.parse(JSON.stringify(res[0]))))
var data = JSON.stringify(res[0])
data = JSON.parse(data).find(x => x)
data ? comparePassword(data.password, req.body.password) : res.status(400).send('cannot find
user')
})
res.send('post login')
}
login.js :
const express = require('express')
const router = express.Router()
const { check } = require('express-validator');
const loginCont = require('../api/controllers/loginController')
router.route('/')
.get(
loginCont.getController
)
.post(
[
check('username').isLength({min: 3}).notEmpty(),
check('password').isLength({min: 4}).notEmpty()
],
loginCont.postController
)
module.exports = router
In my point of view, looks like there is no easy answer for your question so I will try to give you some directions so you can figure out which are the gaps in your code.
First question: MySQL and business logic on controller
In a design pattern like MVC or ADR (please take a look in the links for the flow details) The Controllers(MVC) Or Actions(ADR) are the entry point for the call, and a good practice is to use these entry points to basically:
Instantiate a service/class/domain-class that supports the request;
Call the necessary method/function to resolve what you want;
Send out the response;
This sample project can help you on how to structure your project following a design pattern: https://riptutorial.com/node-js/example/30554/a-simple-nodejs-application-with-mvc-and-api
Second question: db and continue the process
For authentication, I strongly suggest you to take a look on the OAuth or OAuth2 authentication flow. The OAuth(2) has a process where you generate a token and with that token you can always check in your Controllers, making the service a lot easier.
Also consider that you may need to create some external resources/services to solve if the token is right and valid, but it would facilitate your job.
This sample project should give you an example about how to scope your functions in files: https://github.com/cbroberg/node-mvc-api
Summary
You may have to think in splitting your functions into scoped domains so you can work with them in separate instead of having all the logic inside the controllers, then you will get closer to classes/services like: authenticantion, user, product, etc, that could be used and reused amount your controllers.
I hope that this answer could guide you closer to your achievements.

Express Routes & Controllers

I understand that out of the box Express isn't an MVC framework, however I'm trying to set it up like one.
I've used other similar frameworks in PHP like Laravel where in a route in you can use a controller like
Route::get('user/profile', 'UserController#showProfile');
Which will run all the code in the showProfile method in the UserController class,
so my question is, how would I achieve the same thing or something similar using Express?
I'm using Node 5 and writing the code in ECMAScript 6.
Currently I have a class I want to use as the controller and a method I want to return the data, I'm able to log the data to the console when a user navigates to the route but haven't figured out how to send it back as the response.
If you dive into the documentation, you'll find that the "controller methods" you refer to need to conform to a specific signature. Namely, they receive (at least) the request and response representations.
If you have already created a router, this will be a rough equivalent to the PHP you posted:
router.get('user/profile', userController.showProfile)
Your showProfile "method" needs to have this signature:
const userController = {
showProfile(req, res) { /*...*/}
}
I put "method" in quotes because express won't call it as a method unless you explicitly bind it to the controller object. We're passing it as an unbound function here. If you wanted to use it as a method (to have access to the controller as this), pass userController.showProfile.bind(userController) to router.get†.
But for now let's stick to those req and res parameters of the showProfile handler (that's the proper name). req represents the HTTP request, you can get headers, request payload and other stuff from it. res is the HTTP response that will be sent. So you can use it to set an HTTP status code, send body data and so on.
For illustrative purposes, let's assume you can get your user profile synchronously by calling userController.getProfile(id). And let's assume that, by some mechanism, a property userId is set on the request that is the currently authenticated user's ID.
const userController = {
showProfile(req, res) {
// We call some code to get what we want to send back
const profile = userController.getProfile(req.userId)
// We send it in the response as JSON
res.send(profile)
}
}
res.json will JSON.stringify the profile and send it as response payload.
How do you get req.userId set, you ask? Well, this is just an example, but you can achieve similar results by using middleware. A middleware is simply a handler that does something and then lets other handlers continue processing the request. But again, there's plenty to read from the docs.
† It's usually not necessary though, since controllers tend to be singletons. You can simply access its properties by doing userController.otherProperty. Of course, you don't even need to define a handler as a method of a controller object, it can be a function that stands on its own.
I did something like this
usercontroller.js
class UserController() {
constructor() {
this.users = ['user1', 'user2'];
}
getUsers(req, res) {
res.status(200).json(this.users);
}
}
//router.js
const port = 3000;
const app = express();
const _invoke = function(controller) {
return function(req, res) {
const [controllerClass, method] = controller.split('#')
const className = require(controllerClass)
const obj = new className
obj[method](req, res)
}
}
app.get('/get-users',
_invoke('./controllers/UserController#getUsers'));
app.listen(port);

Using the PUT method with Express.js

I'm trying to implement update functionality to an Express.js app, and I'd like to use a PUT request to send the new data, but I keep getting errors using PUT. From everything I've read, it's just a matter of using app.put, but that isn't working. I've got the following in my routes file:
send = function(req, res) {
req.send(res.locals.content);
};
app.put('/api/:company', function(res,req) {
res.send('this is an update');
}, send);
When I use postman to make a PUT request, I get a "cannot PUT /api/petshop" as an error. I don't understand why I can't PUT, or what's going wrong.
You may be lacking the actual update function. You have the put path returning the result back to the client but missing the part when you tell the database to update the data.
If you're using MongoDB and ExpressJS, you could write something like this :
app.put('/api/:company', function (req, res) {
var company = req.company;
company = _.extend(company, req.body);
company.save(function(err) {
if (err) {
return res.send('/company', {
errors: err.errors,
company: company
});
} else {
res.jsonp(company);
}
})
});
This mean stack project may help you as it covers this CRUD functionality which I just used here swapping their articles for your companies. same same.
Your callback function has the arguments in the wrong order.
Change the order of callback to function(req, res).
Don't use function(res, req).
Also if you want to redirect in put or delete (to get adress), you can't use normal res.redirect('/path'), you should use res.redirect(303, '/path') instead. (source)
If not, you'll get Cannot PUT error.
Have you been checking out your headers information?
Because header should be header['content-type'] = 'application/json'; then only you will get the update object in server side (node-express), otherwise if you have content type plain 'text/htm' like that you will get empty req.body in your node app.

Categories

Resources