Ankur Verma
Ankur Verma

Reputation: 5933

JSON schema validation with perfect messages

I have divided the data entry in a REST call in 4 parts. Data can be sent to REST call via:-

  1. headers
  2. query params
  3. path params
  4. request body

So in order to validate the presence of any key in any of the above 4 parts I have created a schema in this format. So if in case I have to validate anything in query params I will add the key 'query' and then add the fields inside that, that needs to be validated

const schema = {
   id: 'Users_login_post',
   type: 'object',
   additionalProperties: false,
   properties: {
     headers: {
     type: 'object',
     additionalProperties: false,
     properties: {
      Authorization: {
        type: 'string',
        minLength: 10,
        description: 'Bearer token of the user.',
        errorMessages: {
          type: 'should be a string',
          minLength: 'should be atleast of 23 length',
          required: 'should have Authorization'
        }
      }
     },
     required: ['Authorization']
   },
   path: {
    type: 'object',
    additionalProperties: false,
    properties: {
      orgId: {
        type: 'string',
        minLength: 23,
        maxLength: 36,
        description: 'OrgId Id of the Organization.',
        errorMessages: {
          type: 'should be a string',
          minLength: 'should be atleast of 23 length', // ---> B
          maxLength: 'should not be more than 36 length',
          required: 'should have OrgId'
        }
      }
    },
    required: ['orgId']
   }
 }
};

Now, in my express code, I created a request object so that I can test the validity of the JSON in this format.

router.get("/org/:orgId/abc", function(req, res){
   var request = { //---> A
       path: {
          orgId : req.params.orgId
       },
       headers: {
          Authorization : req.headers.Authorization
       }
   }
   const Ajv = require('ajv');

   const ajv = new Ajv({
     allErrors: true,
   });

   let result = ajv.validate(schema, request);
   console.log(ajv.errorsText());
});

And I validate the above request object (at A) against my schema using AjV.

The output what I get looks something like this:

data/headers should have required property 'Authorization', data/params/orgId should NOT be shorter than 23 characters

Now I have a list of concerns:

  1. why the message is showing data word in the data/headers and data/params/orgId even when my variable name is request(at A)
  2. Also why not my errormessages are used, like in case of orgId I mentioned: should be atleast of 23 length (at B) as a message, even then the message came should NOT be shorter than 23 characters.
  3. How can I show request/headers instead of data/headers.

Also, the way I used to validate my path params, query params, header params, body param, is this the correct way, if it is not, then what can be the better way of doing the same?

Please shed some light.

Thanks in advance.

Upvotes: 4

Views: 6134

Answers (2)

rofrol
rofrol

Reputation: 15266

Use ajv-keywords

import Ajv from 'ajv';
import AjvKeywords from 'ajv-keywords';
// ajv-errors needed for errorMessage
import AjvErrors from 'ajv-errors';

const ajv = new Ajv.default({ allErrors: true });

AjvKeywords(ajv, "regexp");
AjvErrors(ajv);

// modification of regex by requiring Z https://www.regextester.com/97766
const ISO8601UTCRegex = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?Z$/;

const typeISO8601UTC = {
  "type": "string",
  "regexp": ISO8601UTCRegex.toString(),
  "errorMessage": "must be string of format 1970-01-01T00:00:00Z. Got ${0}",
};

const schema = {
  type: "object",
  properties: {
    foo: { type: "number", minimum: 0 },
    timestamp: typeISO8601UTC,
  },
  required: ["foo", "timestamp"],
  additionalProperties: false,
};

const validate = ajv.compile(schema);

const data = { foo: 1, timestamp: "2020-01-11T20:28:00" }

if (validate(data)) {
  console.log(JSON.stringify(data, null, 2));
} else {
  console.log(JSON.stringify(validate.errors, null, 2));
}

https://github.com/rofrol/ajv-regexp-errormessage-example

Upvotes: 1

customcommander
customcommander

Reputation: 18941

AJV cannot know the name of the variable you passed to the validate function.

However you should be able to work out from the errors array which paths have failed (and why) and construct your messages from there.

See https://ajv.js.org/#validation-errors

To use custom error messages in your schema, you need an AJV plugin: ajv-errors.

See https://github.com/epoberezkin/ajv-errors

Upvotes: 0

Related Questions