@burnsred/entity v0.6.3
Entity
The Entity package provides the fundamental Entity class and it supporting utilities.
An Entity is used to provide cleaned, validated data records.
Defining an Entity
import { Entity, CharField } from "@burnsred/entity";
type User {
username: string;
email: string;
name?: string;
}
const UserEntity = new Entity<User>({
idField: 'username',
fields: {
username: new CharField(),
email: new CharField(),
name: new CharField({ blank: true }),
}
});
Once your entity is defined, you can use it to construct an User
from
API response data:
const rec = UserEntity.fromData(data);
Declaring Fields
Each field can specify its own list of validators and cleaners.
Cleaners are used to cast input values from the User to the right type.
Validators are used to check supplied values, and emit Errors.
fields: {
uuid: new CharField({ cleaners=[trim] }),
size: new IntegerField({ validators=[maxValue(10)] })
}
Common field arguments.
blank (boolean) false : Is this field allowed to be omitted?
cleaners (FieldCleaner[]) : A list of cleaners to apply when cleaning an record.
validators (FieldValidator[]) : A list of validators to apply when validating an record.
many (boolean) false : If true, the value for this field is an array of values of its type.
choices (any) [] : A list of valid choices. Can help with select lists.
defaultValue : Override the field's default value.
Any extra properties passed will be stored in the extra
property.
Cleaners
Cleaner functions are primarily used for cleaning and casting data coming from the User.
Since most HTML Input fields work with string values, many Field
s will
default to using a cleaner which casts to the correct type.
Validators
Validator functions are used to check constraints, emitting FieldErrors to identify problems.
They can be specified either on the Field or Entitly.
Field validators are passed the value for that specific field, and are called
with this
being the Field.
Entity validators are passed the whole record, and are called with this
being
the Entity.
As an example, here is an Entity validator that ensures the start date is before the end date:
function dateRange(record) {
const start = record.get("start_date");
const end = record.get("end_date");
if (start.valueOf() >= end.valueOf()) {
return {
code: "date-range",
message: "Start date must be before End date.",
};
}
}
Errors
Managing and handling validation errors requires records to contain them.
An individual validation error consists of a code
, user meangful message
,
and possibly some additional details
.
For a scalar field, this can be managed as a list of such objects.
For vector values (i.e. many=True) a list of lists will suffice. Extracting errors for a given position is as simple as indexing.
For an EntityField
, a mapping is used, instead. Each value, keyed by
sub-field name, reflects the structure appropriate as described.
IntegerField.validate("1") // [{ code: 'type', message: 'Value must be a number' }]
EntityField.validate({ ... }) // { field: [...], ...}
From the top down, an Entity will get errors by looking into the Error map for the field name.
The values will be either a list of Errors, a list-of-lists of Errors, or another map.
A more complex example
Here's an example showing an Entity with a nested Entity as a field.
type Post = {
id: number;
user: User;
posted: Date;
content: string;
public: boolean;
}
const PostEntity = new Entity<Post>({
fields={
id: IntegerField(),
user: EntityField<Post>({ entity: UserEntity }),
posted: DateField(),
content: TextField(),
public: BooleanField({ defaultVaule: false }),
}
})
Now any record created by this Entity will contain a nested record in the user
field.
const post = PostEntity.fromData(...);
...
const errors = UserEntity.validate(post.user);