Validation Results and Error Helpers
The schema operation methods return an object with two properties: validatedObject and errors.
The validatedObject
This object contains the data after all casting and transformations have been applied. It's the "clean" version of your input that you should use in the rest of your application (e.g., to save to a database).
The errors Object
This is your primary tool for handling validation failures.
- It's a Map, Not an Array: The
errorsobject is a map where keys are the field names that failed. This allows you to instantly check if a specific field has an error:if (errors.age) { ... }. - Rich Error Structure: Each error in the map is a detailed object:
{ code, message, params }. - Nested paths stay flat: Nested fields are reported with dotted paths such as
workspace.slugorroles.2.id. That keeps the external error contract simple even when schemas are recursive.
Let's look at an example with invalid data:
const invalidInput = {
username: 'Al', // Fails 'minLength: 3'
// email is missing, fails 'required: true'
age: 16 // Fails 'min: 18'
};
const { validatedObject, errors } = userSchema.create(invalidInput);
console.log(JSON.stringify(errors, null, 2));The output would look like this:
{
"username": {
"field": "username",
"code": "MIN_LENGTH",
"message": "Length must be at least 3 characters.",
"params": { "min": 3, "actual": 2 }
},
"email": {
"field": "email",
"code": "REQUIRED",
"message": "Field is required",
"params": {}
},
"age": {
"field": "age",
"code": "MIN_VALUE",
"message": "Value must be at least 18.",
"params": { "min": 18, "actual": 16 }
}
}code: A stable, machine-readable string. Use this in your code for logic (if (err.code === 'MIN_LENGTH')).message: A human-readable message, great for developers or for displaying directly to users in simple cases.params: Extra context about the failure. This is incredibly useful for creating dynamic error messages (e.g., "You entered 2 characters, but a minimum of 3 is required.").
Error helper utilities
If you want a few adapter-friendly utilities around that flat error map, import them directly:
import { createSchema, getError, hasError, nestErrors, flattenErrors } from 'json-rest-schema'getError(errors, path) reads one dotted-path error:
const slugError = getError(errors, 'workspace.slug')hasError(errors, path) is the small boolean version:
const showSlugError = hasError(errors, 'workspace.slug')nestErrors(errors) converts the flat map into a nested object/array shape for form libraries that prefer nested field errors:
nestErrors({
'workspace.slug': {
field: 'workspace.slug',
code: 'MIN_LENGTH',
message: 'Length must be at least 3 characters.',
params: { min: 3, actual: 1 }
},
'roles.2.label': {
field: 'roles.2.label',
code: 'REQUIRED',
message: 'Field is required',
params: {}
}
})Result:
{
workspace: {
slug: {
field: 'workspace.slug',
code: 'MIN_LENGTH',
message: 'Length must be at least 3 characters.',
params: { min: 3, actual: 1 }
}
},
roles: [
,
,
{
label: {
field: 'roles.2.label',
code: 'REQUIRED',
message: 'Field is required',
params: {}
}
}
]
}That keeps the runtime contract flat, while still making it easy to adapt into nested UI-state libraries.
flattenErrors(nestedErrors) does the reverse when a UI layer gives you nested field errors and you want to normalize them back into the library's flat contract:
flattenErrors({
workspace: {
slug: {
field: 'workspace.slug',
code: 'MIN_LENGTH',
message: 'Length must be at least 3 characters.',
params: { min: 3, actual: 1 }
}
},
roles: [
,
,
{
label: {
field: 'roles.2.label',
code: 'REQUIRED',
message: 'Field is required',
params: {}
}
}
]
})Result:
{
'workspace.slug': {
field: 'workspace.slug',
code: 'MIN_LENGTH',
message: 'Length must be at least 3 characters.',
params: { min: 3, actual: 1 }
},
'roles.2.label': {
field: 'roles.2.label',
code: 'REQUIRED',
message: 'Field is required',
params: {}
}
}