Skip to content

Commit

Permalink
docs: explain how to overwrite cast error messages in validation docs
Browse files Browse the repository at this point in the history
Re: #3162
  • Loading branch information
vkarpov15 committed Jul 14, 2023
1 parent 57a5db5 commit d6cf0a0
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 12 deletions.
30 changes: 23 additions & 7 deletions docs/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,35 @@ thrown.

## Cast Errors

Before running validators, Mongoose attempts to coerce values to the
correct type. This process is called *casting* the document. If
casting fails for a given path, the `error.errors` object will contain
a `CastError` object.
Before running validators, Mongoose attempts to coerce values to the correct type. This process is called *casting* the document.
If casting fails for a given path, the `error.errors` object will contain a `CastError` object.

Casting runs before validation, and validation does not run if casting
fails. That means your custom validators may assume `v` is `null`,
`undefined`, or an instance of the type specified in your schema.
Casting runs before validation, and validation does not run if casting fails.
That means your custom validators may assume `v` is `null`, `undefined`, or an instance of the type specified in your schema.

```acquit
[require:Cast Errors]
```

By default, Mongoose cast error messages look like `Cast to Number failed for value "pie" at path "numWheels"`.
You can overwrite Mongoose's default cast error message by the `cast` option on your SchemaType to a string as follows.

```acquit
[require:Cast Error Message Overwrite]
```

Mongoose's cast error message templating supports the following parameters:

- `{PATH}`: the path that failed to cast
- `{VALUE}`: a string representation of the value that failed to cast
- `{KIND}`: the type that Mongoose attempted to cast to, like `'String'` or `'Number'`

You can also define a function that Mongoose will call to get the cast error message as follows.

```acquit
[require:Cast Error Message Function Overwrite]
```

## Global SchemaType Validation

In addition to defining custom validators on individual schema paths, you can also configure a custom validator to run on every instance of a given `SchemaType`.
Expand Down
2 changes: 1 addition & 1 deletion lib/error/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* const mongoose = require('mongoose');
* mongoose.Error.messages.String.enum = "Your custom message for {PATH}.";
*
* As you might have noticed, error messages support basic templating
* Error messages support basic templating. Mongoose will replace the following strings with the corresponding value.
*
* - `{PATH}` is replaced with the invalid document path
* - `{VALUE}` is replaced with the invalid value
Expand Down
47 changes: 47 additions & 0 deletions test/docs/validation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,53 @@ describe('validation docs', function() {
// acquit:ignore:end
});

it('Cast Error Message Overwrite', function() {
const vehicleSchema = new mongoose.Schema({
numWheels: {
type: Number,
cast: '{VALUE} is not a number'
}
});
const Vehicle = db.model('Vehicle', vehicleSchema);

const doc = new Vehicle({ numWheels: 'pie' });
const err = doc.validateSync();

err.errors['numWheels'].name; // 'CastError'
// "pie" is not a number
err.errors['numWheels'].message;
// acquit:ignore:start
assert.equal(err.errors['numWheels'].name, 'CastError');
assert.equal(err.errors['numWheels'].message,
'"pie" is not a number');
db.deleteModel(/Vehicle/);
// acquit:ignore:end
});

/* eslint-disable no-unused-vars */
it('Cast Error Message Function Overwrite', function() {
const vehicleSchema = new mongoose.Schema({
numWheels: {
type: Number,
cast: [null, (value, path, model, kind) => `"${value}" is not a number`]
}
});
const Vehicle = db.model('Vehicle', vehicleSchema);

const doc = new Vehicle({ numWheels: 'pie' });
const err = doc.validateSync();

err.errors['numWheels'].name; // 'CastError'
// "pie" is not a number
err.errors['numWheels'].message;
// acquit:ignore:start
assert.equal(err.errors['numWheels'].name, 'CastError');
assert.equal(err.errors['numWheels'].message,
'"pie" is not a number');
db.deleteModel(/Vehicle/);
// acquit:ignore:end
});

it('Global SchemaType Validation', async function() {
// Add a custom validator to all strings
mongoose.Schema.Types.String.set('validate', v => v == null || v > 0);
Expand Down
8 changes: 4 additions & 4 deletions types/schematypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ declare module 'mongoose' {

/** Allows overriding casting logic for this individual path. If a string, the given string overwrites Mongoose's default cast error message. */
cast?: string |
boolean |
((value: any) => T) |
[(value: any) => T, string] |
[((value: any) => T) | null, (value: any, path: string, model: Model<T>, kind: string) => string];
boolean |
((value: any) => T) |
[(value: any) => T, string] |
[((value: any) => T) | null, (value: any, path: string, model: Model<T>, kind: string) => string];

/**
* If true, attach a required validator to this path, which ensures this path
Expand Down

0 comments on commit d6cf0a0

Please sign in to comment.