Build your own rule
When the package has no validator or formatter for your field, build one from the package's building blocks: a base rule or base formatter, plus options. You don't need new code in the package or in your service.
Before you begin
- Check that the package doesn't already have what you need. For validators, see validators(). For formatters, see formatters().
- Know what the field must accept: the characters, the length, the number range, or the date range.
Choose the building blocks
A rule is a starting point plus options:
| Starting point | Use it when | Examples |
|---|---|---|
| A base rule | The value is a kind of thing the package already checks, such as a number, digits, or a date | number, numbersOnly, date |
| A preset | A preset is close, but needs a tighter limit | integer with minValue: 1 |
null | The value has its own format, which you describe with regex | A container number |
The options add the checks:
- Length:
length,minLength, andmaxLength - Numbers:
minValue,maxValue, anddecimalPlaces - Format:
regex - Dates:
minDate,maxDate,after, andbefore - Message:
message, which replaces the default message
For every base rule and option, see validators().
Build a validator
- Pick the starting point that is closest to your field.
- Add the options that narrow it to what the field accepts.
- Set
messageto text that tells the user what to enter.
The following examples build validators for fields that have no preset:
// An 8-digit HS code: digits only, exactly 8 of them
validators(value, "numbersOnly", { length: 8, message: "Enter an 8-digit HS code" })
// A temperature from -40 to 30, with up to one decimal
validators(value, "number", { minValue: -40, maxValue: 30, decimalPlaces: 1, message: "Enter a temperature from -40 to 30" })
// A quantity of at least 1
validators(value, "integer", { minValue: 1 })
// A delivery date within the next 7 days
validators(value, "date", { minDate: "today", maxDate: { addDays: 7 }, message: "Choose a date in the next 7 days" })
// A container number: four letters and seven digits
validators(value, null, { regex: "^[A-Z]{4}[0-9]{7}$", message: "Enter a valid container number" })
When a check fails, the result's code names the check, such as length or regex.
The message is the one that you set.
In regex, don't use \d, \w, or \s; the call throws an error if you do.
Write [0-9], [A-Za-z0-9_], and [ \t] instead, so that the pattern gives the same answer in JavaScript and Python.
Build a formatter
Pair the validator with a formatter that keeps the field clean while the user types. Start from a base formatter, and add these options:
allowChars: keep only these characters, written as a character set such as"A-Za-z0-9"maxLength: cut the value to this many charactersdecimalPlacesandallowNegative: shape a decimal number
The following formatters match the validators in the previous section:
// HS code: digits only, at most 8
formatters(value, "numbersOnly", { maxLength: 8 })
// Temperature: digits, one decimal point, and a leading minus sign, with one decimal place
formatters(value, "decimal", { allowNegative: true, decimalPlaces: 1 })
// Container number: letters and digits only, in upper case, at most 11
formatters(value, "upperCase", { allowChars: "A-Za-z0-9", maxLength: 11 })
For every base formatter and option, see formatters().
Keep the rule in one place
Write each rule once, as data, and use it on every screen and in every API that handles the field. Then the browser and the server check the field the same way.
// fieldRules.js
export const fieldRules = {
containerNumber: {
format: { name: "upperCase", options: { allowChars: "A-Za-z0-9", maxLength: 11 } },
validate: { name: null, options: { regex: "^[A-Z]{4}[0-9]{7}$", message: "Enter a valid container number" } },
},
};
import { formatters, validators } from "@enmovil/common-utils";
import { fieldRules } from "./fieldRules";
const rule = fieldRules.containerNumber;
const shown = formatters(raw, rule.format.name, rule.format.options);
const result = validators(shown, rule.validate.name, { ...rule.validate.options, required: true });
Because the rule is plain data, you can also keep it in a JSON screen config.
In a Python service, pass the same options as snake_case keywords, such as max_length=11.
Checks that need your own data
Some checks aren't about the value's format, such as whether a purchase order (PO) exists. Do these checks in your service's own code, after the value passes its rule.