Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

schema validation fails although parameter is available

I created a REST api and want to validate the body and the params before calling the controller logic. For the validation I use Joi (https://www.npmjs.com/package/joi).

Let's say I have a route with one url parameter and some body variables. The params object contains this url parameter but Joi still returns a 400. The detail message is

"userId" is required

I tried to create a minimalistic example showing my code. To reproduce the error create the app.js file

const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const cors = require('cors');

const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());

app.use('/users', require('./routes/users.js'));

app.listen(3000);

Due to the fact each validation fails there is only one route required to test it. Create users.js with the following content

const express = require('express');
const router = express.Router();

const usersController = require('../controllers/users.js');
const usersControllerPolicy = require('../policies/users.js');

router.get('/:userId', usersControllerPolicy.getUserById, usersController.getUserById);

module.exports = router;

And this users.js controller file

exports.getUserById = async (req, res, next) => {
    const { userId } = req.params;
    return res.status(200).json("everything is fine");
};

When it comes to the policy I created the users.js policy which adds the required schema to the middleware

const joi = require('joi');

const schemaValidation = require('../middleware/schemaValidation.js');

module.exports = {
    getUserById: (req, res, next) => {
        schemaValidation({
            params: {
                userId: joi.string().guid().required()
            },
            body: {}
        }, req, res, next);
    }
}

and then the schema gets validated by my schemaValidation.js

const joi = require('joi');

module.exports = (schema, req, res, next) => {
    const { error } = joi.validate(req, schema);

    if (error)
        return res.status(400).json("something went wrong");

    next(); // execute the controller logic
}

As you can see I pass in the whole req object. I do this because sometimes I have to validate the body and the params. The url parameter userId is not found by Joi so I get returned a status code of 400.

How can I fix my middleware validation to validate both objects within the req object?

like image 321
Question3r Avatar asked Oct 16 '22 05:10

Question3r


1 Answers

Actually Joi has access to the userId and can properly validate it or not, here's why:

// replace this
const { error } = joi.validate(req, schema);
// by this
console.log(req.params.userId);
const { error } = joi.validate(req, schema);
console.log(error.toString());

console output while visiting localhost:3000/users/10:

10
ValidationError: child "params" fails because [child "userId" fails because ["userId" must be a valid GUID]]

and while visiting an URL including a valid GUID in the params, like ``:

ad3756ae-2661-4d8c-aeda-dd51deef5ea9
ValidationError: "_readableState" is not allowed. "readable" is not allowed. "_events" is not allowed. "_eventsCount" is not allowed. "_maxListeners" is not allowed. "socket" is not allowed. "connection" is not allowed. "httpVersionMajor" is not allowed. "httpVersionMinor" is not allowed. "httpVersion" is not allowed. "complete" is not allowed. "headers" is not allowed. "rawHeaders" is not allowed. "trailers" is not allowed. "rawTrailers" is not allowed. "aborted" is not allowed. "upgrade" is not allowed. "url" is not allowed. "method" is not allowed. "statusCode" is not allowed. "statusMessage" is not allowed. "client" is not allowed. "_consuming" is not allowed. "_dumped" is not allowed. "next" is not allowed. "baseUrl" is not allowed. "originalUrl" is not allowed. "_parsedUrl" is not allowed. "query" is not allowed. "res" is not allowed. "route" is not allowed

So Joi has access to everything that it needs and works as expected. 🚀


So why the error 400?

Well, as logged in the console, Joi fails the validation for req. That is because, by default, Joi doesn't accept unknown params in an object. You can change this with .unknown(true) which validates an object even if unknown parameters are included.

So, in your case, you should replace the code of policies/users.js by the following:

const joi = require('joi');

const schemaValidation = require('../middleware/schemaValidation.js');

module.exports = {
    getUserById: (req, res, next) => {

        // properly defines a schema with optional parameters
        const schema = joi.object({
            params: joi.object({
                userId: joi.string().guid().required()
            }).unknown(true),
        }).unknown(true);

        schemaValidation(schema, req, res, next);
    }
}

Now, when you visit again an URL including a valid GUID (like http://localhost:3000/users/ad3756ae-2661-4d8c-aeda-dd51deef5ea9), everything happens as expected and ""everything is fine" is being sent by the server! 🎉

like image 150
Nino Filiu Avatar answered Oct 21 '22 11:10

Nino Filiu