@commodo/fields
Enables defining rich data models by decorating function instances with specified model fields. Additionally, it adds populate
and validate
methods, for populating model instances with data, and then validating it, respectively.
Usage
import { withFields, string, number, boolean, fields } from "@commodo/fields";
import { compose } from "ramda";
const User = compose(
withFields({
email: string(),
previousEmails: string({ list: true }),
verified: boolean(),
company: fields({ instanceOf: Company }),
age: number({
validation: value => {
if (value < 30) {
throw Error("User too young.")
}
}
})
})
)();
const Company = compose(
withFields({
name: string()
})
)();
const user = new User();
user.populate({
email: "user3@email.com",
previousEmails: ["user2@email.com", "user1@email.com"],
age: 25,
verified: true,
company: {
name: "Awesome Company"
}
});
async user.validate();
Available fields
Out of the box, there are four types of fields you can utilize:
string
- accepts string valuesnumber
- accepts number valuesboolean
- accepts boolean valuesfields
- accepts a plain object or an instance of another withFields
function
In the following examples, all types of fields are utilized:
const Company = compose(
withFields({
name: string()
})
)();
const User = compose(
withFields({
email: string(),
age: number(),
verified: boolean(),
company: fields({ instanceOf: Company })
})
)();
Data validation
Data-type validation
When a value is assigned to a field of a model instance, it is immediately validated on a data-type level, meaning you cannot pass a string value to a field that doesn't accept strings.
Consider the following example:
import { withFields, string, number } from "@commodo/fields";
const User = withFields({
name: string(),
age: number(),
})();
const user = new User();
user.age = "7";
user.populate({ name: "Rex", age: "7", drools: false });
Data-type validation is always executed upon value assignment, synchronously.
Custom validation
Additionally, you can also add your own custom, business logic related, validation. Unlike the data-type validation, which happens immediately upon assigning the value to a field, the custom validation is triggered by calling the validate
method. Note that this method validates the whole model instance.
The following snippet shows how we can add your own custom validation and trigger it:
import { withFields, string, number } from "@commodo/fields";
const User = withFields({
name: string({
validate: value => {
if (!value) {
throw new Error("Name is required.");
}
}
}),
age: number({
validate: value => {
if (value && value < 2) {
throw new Error("Your dog is to young.");
}
}
})
})();
const user = new User();
user.populate({ name: "Rex", age: 1 });
await user.validate();
user.populate({ age: 2 });
await user.validate();
Unlike the data-type validation, custom validation can perform asynchronous operations.
Field options
Each field can accept a few options:
list: boolean
If set to true
, field will accept an null
or an array of values. When setting field value, if a single item in the passed array is of incorrect data type, an error will be thrown.
validation: Function
A function for validating the assigned value. Not for data-type validation (since it's already done upon assigning a value), but for checking if the value complies with custom logic, for example if the assigned value is greater than 20.
value: any
Additional higher order functions
Except options, fields can also be enhanced with a couple of provided higher order functions:
onGet: Function => Function
onSet: Function => Function
skipOnPopulate: Function => Function
setOnce: Function => Function
Reference
withFields(fields : { [string] : FieldFactory }): WithFieldsFunction
Creates a new function, whose instances contain defined fields and are decorated with a couple of useful methods.
### FieldFactory
WithFieldsFunction
Except fields, instances of WithFieldsFunction
are decorated with a couple of useful methods.
populate(data: Object): void
Populates fields with given data.
validate(): Promise<void>
Validates all fields.
getFields(): Array<Field>
Returns all fields.
getField(name: string): ?Field
Returns a single field.
clean(): void
Sets instance as clean.
isDirty(): boolean
Checks if instance is dirty.