-
Notifications
You must be signed in to change notification settings - Fork 1
core HowTo
GitHub Action edited this page Apr 21, 2026
·
1 revision
This guide provides practical examples for using the @quatrain/core package, covering basic to advanced use cases.
- Basic Object Creation
- Advanced Property Validation
- Managing Object Lifecycles
- Using the ObjectURI System
- Best Practices
In Quatrain, any business entity should extend BaseObject. This provides property validation, tracking, and serialization.
import {
BaseObject,
StringProperty,
NumberProperty,
BooleanProperty
} from '@quatrain/core'
export class Customer extends BaseObject {
static COLLECTION = 'customers'
// Define the schema using PROPS_DEFINITION
static PROPS_DEFINITION = [
{ name: 'firstName', type: StringProperty.TYPE, mandatory: true },
{ name: 'lastName', type: StringProperty.TYPE, mandatory: true },
{ name: 'email', type: StringProperty.TYPE, mandatory: true },
{ name: 'age', type: NumberProperty.TYPE },
{ name: 'isPremium', type: BooleanProperty.TYPE, defaultValue: false }
]
}
// Usage Example
async function createCustomer() {
// Always use the factory() method, never new Customer()
const customer = await Customer.factory()
// Use the `_` proxy to interact with properties
customer._.firstName = 'John'
customer._.lastName = 'Doe'
customer._.email = 'john.doe@example.com'
// Check if the object passes all property validations
if (customer.isValid()) {
console.log('Customer data is valid!')
console.log(customer.toJSON())
}
}Properties in Quatrain aren't just types; they enforce constraints automatically.
import { BaseObject, StringProperty, NumberProperty, ArrayProperty } from '@quatrain/core'
export class Product extends BaseObject {
static COLLECTION = 'products'
static PROPS_DEFINITION = [
{
name: 'sku',
type: StringProperty.TYPE,
mandatory: true,
// You can restrict maximum length
length: 12
},
{
name: 'price',
type: NumberProperty.TYPE,
// You can specify min/max constraints
min: 0.01,
max: 10000
},
{
name: 'tags',
type: ArrayProperty.TYPE,
// Initialize with empty array instead of null
defaultValue: []
}
]
}
async function updateProduct(productData: any) {
const product = await Product.factory()
product._.sku = 'VERY-LONG-SKU-NAME-THAT-WILL-FAIL' // Will throw an error on validation
product._.price = -5 // Invalid, minimum is 0.01
// You can catch specific property errors
try {
product.validate() // Throws if any mandatory or constrained property fails
} catch (err) {
console.error("Validation failed:", err.message)
}
}Every BaseObject includes a built-in status property (from the statuses enum) which dictates its lifecycle stage.
import { BaseObject, statuses } from '@quatrain/core'
async function processOrder() {
const order = await Order.factory()
// By default, new objects are in 'CREATED' status
console.log(order.status === statuses.CREATED) // true
// Move to pending
order.set('status', statuses.PENDING)
// Move to active once processed
order.set('status', statuses.ACTIVE)
// Soft delete
order.set('status', statuses.DELETED)
}The ObjectURI is a core concept in Quatrain that allows you to uniquely identify any resource across collections and adapters, even without a persistent database.
import { ObjectURI } from '@quatrain/core'
// Format: quatrain://[backend]/[collection]/[id]
const uri = new ObjectURI('quatrain://firestore/users/12345')
console.log(uri.backend) // 'firestore'
console.log(uri.collection) // 'users'
console.log(uri.path) // '12345'
console.log(uri.fqdn) // 'quatrain://firestore/users/12345'
// Generate a new unique URI for a specific collection
const newUri = ObjectURI.generate('products')
console.log(newUri.fqdn) // e.g. quatrain://memory/products/a8b9c...-
Always use
.factory(): Never instantiate aBaseObjectdirectly usingnew MyClass(). The.factory()method ensures proper initialization of proxies and internal states. -
Use the
_proxy: Whileobj.get('prop')andobj.set('prop', val)work, using the proxyobj._.prop = valis cleaner and provides TypeScript auto-completion. -
Avoid Persistent Logic: Keep in mind that
@quatrain/coreruns entirely in memory. If you need objects to interact with a database, your classes must extendPersistedBaseObjectfrom the@quatrain/backendpackage instead ofBaseObject.