How to get property name from ajv output to map validation errors to human friendly messages?
Asked Answered
R

1

6

I am using AJV library https://github.com/ajv-validator/ajv to validate the input of my nodejs express api. However, im having trouble extracting the property name in question for each error object in the returned array.

[{
  instancePath: '',
  schemaPath: '#/required',
  keyword: 'required',
  params: { missingProperty: 'start_date' },
  message: "must have required property 'start_date'"
}
{
  instancePath: '/top',
  schemaPath: '#/properties/top/type',
  keyword: 'type',
  params: { type: 'number' },
  message: 'must be number'
}]

As you can see from the above output extracting the property name (start_date, top) for each is a little different, so im hoping there is an easy way to do that without having to parse depending on the error type (keyword).

Expectation

i expect to be able to create a error like below mapping the original array. To do that i need the message which is available in the above original output and the property name which is not available.

[
  {  property: "start_date", message: "must have required property 'start_date"}
  {  property: "top", message: "must be number" },
]

Code

export interface ILeaderboardQuery {
    rank: string;
    entity_types: string[];
    country?: string | undefined;
    region?: string | undefined;
    start_date: string;
    end_date: string;
    top?: number | undefined;
}

export const LeaderboardQuerySchema: JSONSchemaType<ILeaderboardQuery> = {
    type: "object",
    properties: {
        rank: { type: "string" },
        entity_types: {
            type: "array",
            items: {
                type: "string",
            },
        },
        country: { type: "string", nullable: true },
        region: { type: "string", nullable: true },
        start_date: { type: "string" },
        end_date: { type: "string" },
        top: { type: "number", nullable: true },
    },
    required: ["rank", "start_date", "end_date"],
    additionalProperties: false,
};

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

export const GetLeaderboardValidator = (req: Request, res: Response, next: NextFunction) => {
    const validate = ajv.compile<ILeaderboardQuery>(LeaderboardQuerySchema);

    for (const err of validate.errors as DefinedError[]) {
        console.log(err);
    }
};

ajv: ^8.6.2"

Raceway answered 19/7, 2021 at 13:50 Comment(0)
L
0

You can use the ajv-errors package if you don't mind adding extra non-JSON Schema lingo into your schema.

You can add an extra errorMessage to any schema. It can be set to either a string or an object. If set to an object its keys are set to rules and values set to error messages.

Hopefully this makes sense:

const Ajv = require('ajv');
const ajvErrors = require('ajv-errors');

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

const validate = ajv.compile({
  type: "object",
  required: ["foo"],
  additionalProperties: false,
  properties: {
    foo: {
      type: "number",
      errorMessage: "CUSTOM ERROR: foo must be a number"
    }
  },
  errorMessage: {
    type: "CUSTOM ERROR: not an object",
    required: "CUSTOM ERROR: missing required property foo",
    additionalProperties: "CUSTOM ERROR: cannot have other properties"
  }
});


validate("foo");
validate.errors;

/*
[
  {
    instancePath: '',
    schemaPath: '#/errorMessage',
    keyword: 'errorMessage',
    params: { errors: [Array] },
    message: 'CUSTOM ERROR: not an object'
  }
]
*/

validate({});
validate.errors;

/*
[
  {
    instancePath: '',
    schemaPath: '#/errorMessage',
    keyword: 'errorMessage',
    params: { errors: [Array] },
    message: 'CUSTOM ERROR: missing required property foo'
  }
]
*/

validate({foo: 1, bar: 2});
validate.errors;

/*
[
  {
    instancePath: '',
    schemaPath: '#/errorMessage',
    keyword: 'errorMessage',
    params: { errors: [Array] },
    message: 'CUSTOM ERROR: cannot have other properties'
  }
]
*/

validate({foo: "wat"});
validate.errors;

/*
[
  {
    instancePath: '/foo',
    schemaPath: '#/properties/foo/errorMessage',
    keyword: 'errorMessage',
    params: { errors: [Array] },
    message: 'CUSTOM ERROR: foo must be a number'
  }
]
*/
Levison answered 19/7, 2021 at 14:46 Comment(3)
Does not seem like this library provides me with a way to separate the property name, atleast i can't see that in any of the examples you have shownRaceway
You can add an errorMessage in each schema and customise them there. Additional the property name is in the schemaPath as long as it has failed on the path.Levison
Right so this kind of goes back to my original point of trying to avoid parsing strings depending on keyword / error types. I don't need the functionality of this extention because all i really wanted was to seperate the property name and it sounds like using this extention i still would need to do the parsing.Raceway

© 2022 - 2025 — McMap. All rights reserved.