Promise Validation
Promise Validation in JavaScript library which uses promises in a
functional-like manner. It is light weight, composable, and really simple to
use.
Installation
npm install promise-validation
Or just copy the file and paste it into your project.
Top-level View
A composable way to validate and create objects. This is how you would create a
validator which will validate and transform your data to what you would like.
const nameValidator = {
firstName: createString25('First Name'),
lastName: maybe(createString25('Last Name'))
}
const personValidator = {
...name,
birthdate: createDate('Birthdate')
}
You can see how you can compose the validators to build new validators. You can
combine validators like maybe with createString25 — this will say that the
string doesn't necessarily need to exist (it can be null or undefined) but
if it is something will validate that it is a string of maximum length of 25.
Then the name validator is composed into the person validator which adds the
birthdate validation.
Then when you pass it to the function to do the actual validation:
const rawData = {
firstName: "George",
lastName: "Jungle",
birthdate: "1967-09-09"
}
const person = await validateObject(rawData, personValidator)
console.log(person)
But what happens when the validation fails? Let us make one that fails for all
properties:
const rawData = {
lastName: "Jungle".repeat(5),
birthdate: "1967-99-99"
}
const failed = await validateObject(rawData, personValidator).catch(x => x)
console.log(failed)
So, how would you normally do this?
const person = validatePerson(data)
if (person == null) {
return null
}
const firstName = person?.firstName ?? "No name"
Rather messy. But with the validation above you can know that if the async
function doesn't return an error that all your values are what they say they
are.
More Details
So, how do we create the validation components?
function fail(message: string) {
return Promise.reject(message)
}
function required<T>(name: string, value: T) : Promise<T> {
return !value ? fail(`"${name}" is required but is falsey.`) : value
}
async function createString(name: string, value: string, length: number) {
const v = await required(name, value?.trim())
return v.length <= length ? v : fail(`${name} is longer than ${length} characters.`)
}
const createString25 =
(name: string) =>
(value: string | undefined) =>
createString(name, value, 25)
const maybe =
<T>(f: (val: T | undefined) => Promise<T>) =>
(val: T | undefined) =>
!val ? Promise.resolve(val) : f(val)
const createDate =
(name: string) =>
async (value: string | Date) => {
let v = await required(name, value)
if (v instanceof Date) {
if (isNaN(+v)) {
return fail(`"${name}" is not a valid date.`)
}
return v
}
let d = new Date(v)
if (isNaN(+d)) {
return fail(name, `is not a valid date.`)
}
return d
}
As you can see, you can build up all the different validations just like you
want to make them. They can be composable with high reuse of code in a
functional and declarative way. No need for if statements. Just a simple
async-await pattern gets you where you need to be!
How to handle an array of data?
Glad you asked. You could do it like so:
var results = Promise.all(data)
var results = validate(data)
var results = Promise.allSettled(data)
Change Log
4.0.0
- Removed
reason field from ValidationResult class.