Recipes
Required, optional, and computed fields
import { Entity, Fields, Flags, QEntity } from '@quik/entity';
@Entity('Profile')
export class Profile extends QEntity {
@Fields.String()
@Flags.Required
name: string;
@Fields.String()
@Flags.Optional
bio?: string;
@Fields.String()
@Flags.ReadOnly
slug: string;
}
Flags.Required enforces non-null values during validation; Flags.Optional allows null/undefined and skips validators when unset; Flags.ReadOnly prevents the field from being assigned during fill().
Deriving a field on fill
import { Entity, Fields, Hooks, QEntity } from '@quik/entity';
@Entity('Profile')
class Profile extends QEntity {
@Fields.String()
name!: string;
@Fields.String()
slug!: string;
@Hooks.onAfterFill
deriveSlug() {
this.slug = this.name.toLowerCase().replace(/\s+/g, '-');
}
}
Hooks.onBeforeFill/onAfterFill run during fill() — use them for defaulting or derived fields instead of overriding fill() itself.
Sanitizing input
import { Entity, Fields, Sanitizers, QEntity } from '@quik/entity';
@Entity('Contact')
class Contact extends QEntity {
@Fields.Email()
@Sanitizers.Email()
email: string;
@Fields.String()
@Sanitizers.Trim()
name: string;
}
Sanitizer decorators (Trim, Escape, Slugify, Whitelist, Blacklist, ...) run against the raw value during fill(), before validators.
Selectable fields
@Entity('Product')
class Product extends QEntity {
@Fields.String()
@Flags.Selectable
sku: string;
}
const product = EntityStore.create('Product', { sku: 'LT-1234' });
product.selectableFields; // ['sku']
selectableFields (and the underlying getFlaggedField('selectable')) collects every field decorated with Flags.Selectable, for building query/select-list helpers.
Cloning and comparing entities
const clone = EntityStore.clone(user);
const isSame = user.compare(clone); // deep-equal comparison via toJSON()