5

I have a problem of validation JSON that is passed in GET request query param as a serialized string.

What I need to achieve is to parse this serialized string back to JSON and validated it using Joi.

Example: Give is the JSON

{
 limit: {size:10, page:0},
 filter: {filter_by: 'foo', filter_val: 'foo', from: '1/1/2016',to: '1/1/2016' }
}

And this JSON is converted to query string is:

limit%5Bsize%5D=10&limit%5Bpage%5D=0&filter%5Bfilter_by%5D=foo&filter%5Bfilter_val%5D=foo&filter%5Bfrom%5D=1%2F1%2F2016&filter%5Bto%5D=1%2F1%2F2016

I need something like this to check:

 validate: {
          query: {
            limit: Joi.someMethodToGetJsonFromString.object().keys({
              size: Joi.number(),
              page: Joi.number()
            }
          filter: Joi.someMethodToGetJsonFromString,.object().keys({
              filter_by: Joi.string().valid(['option1', 'option2']),
              filter_val: Joi.string(),
              from: Joi.date(),
              to: Joi.date(),
            }
        }

Is there anything in Joi that can help in this scenario, Or I need to write custom validation functions for it.

1 Answer 1

4

You'll want to swap out the query string parsing used because hapi doesn't support that format by default. It just uses Node's built in url module to parse the URL which doesn't support complex encodings of query strings. You'll need to manually parse it using qs, after that you can validate as normal.

Hapi Version ≥18

You can set your custom parser via server.options.query.parser.

Hapi Version ≤17

const Hapi = require('hapi');
const Joi = require('joi');
const Url = require('url');
const Qs = require('qs');

const server = new Hapi.Server();
server.connection({ port: 4000 });

const onRequest = function (request, reply) {

    const uri = request.raw.req.url;
    const parsed = Url.parse(uri, false);   // skip the querystring parsing
    parsed.query = Qs.parse(parsed.query);  // parse it ourselves with qs
    request.setUrl(parsed);

    return reply.continue();
};

server.ext('onRequest', onRequest);

server.route({
    config: {
        validate: {
            query: {
                limit: Joi.object().keys({
                    size: Joi.number(),
                    page: Joi.number()
                }),
                filter: Joi.object().keys({
                    filter_by: Joi.string().valid(['option1', 'option2']),
                    filter_val: Joi.string(),
                    from: Joi.date(),
                    to: Joi.date(),
                })
            }
        }
    },
    method: 'GET',
    path: '/',
    handler: function (request, reply) {

        return reply('ok');
    }
});

server.start((err) => {

    if (err) {
        throw err;
    }
    console.log('Started server');
});

This is discussed a little bit in the API docs under the request.setUrl() method.

Sign up to request clarification or add additional context in comments.

2 Comments

exactly what I was looking for. Thanks,
I think you could just use Qs against request.query without worrying about the URL - i.e. Qs.parse(request.query)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.