Data Models
BasicDataModel
The BasicDataModel function creates a model class for a specific table. It wraps the underlying AQL table with methods that automatically apply modifier transformations.
import { BasicDataModel } from "@antelopejs/interface-database-decorators";
import { User } from "./user.table";
const UserModel = BasicDataModel(User, "users");
Parameters
| Parameter | Type | Description |
|---|---|---|
dataType | Constructible<T> | The Table class representing your table |
tableName | string | The table name in the database (optional if @RegisterTable is used) |
When the table class has been decorated with @RegisterTable, the tableName parameter can be omitted.
Instantiate a Model
A model instance requires a SchemaInstance from the database interface:
import { Schema } from "@antelopejs/interface-database";
const schema = Schema.get("myapp")!;
const db = schema.instance();
const userModel = new UserModel(db);
CRUD Operations
Read
// Get by primary key
const user = await userModel.get("user-123");
// Get by secondary index
const admins = await userModel.getBy("role", "admin");
// Get all documents
const allUsers = await userModel.getAll();
Create
// Insert a single document
const ids = await userModel.insert({
email: "[email protected]",
name: "Alice",
});
// Insert multiple documents
const batchIds = await userModel.insert([
{ email: "[email protected]", name: "Bob" },
{ email: "[email protected]", name: "Carol" },
]);
Update
// Update by ID
await userModel.update("user-123", { name: "Updated Name" });
// Update by including the primary key in the object
await userModel.update({ _id: "user-123", name: "Updated Name" });
Important: When calling
updatewithout a separate ID argument, the object must include the primary key field.
Delete
await userModel.delete("user-123");
Data Conversion
Data models include static methods for converting between different data representations:
fromPlainData(obj)
Convert a plain JavaScript object into a Table class instance. Modifier transformations (like encryption and hashing) are applied during this conversion.
const user = UserModel.fromPlainData({
_id: "user-123",
email: "[email protected]",
name: "John",
});
fromDatabase(obj)
Convert a raw database record into a Table class instance. This attaches the class prototype and triggers the fromDatabase modifier event.
const user = UserModel.fromDatabase(rawDbRecord);
toDatabase(obj)
Convert a Table class instance into a plain object suitable for database storage. Modifier events like toDatabase are triggered.
const dbReady = UserModel.toDatabase(userInstance);
Validation
Model.validate(obj) runs every io-ts codec attached via @Field against the matching property of obj. String-token fields and properties without @Field are skipped.
const result = UserModel.validate({ name: "Alice", age: 30 });
if (!result.ok) {
console.error(result.errors); // [{ field: "age", message: "not number" }, ...]
}
Pass { validate: true } to insert or update to gate the write on validation. On failure the call throws an Error whose errors property holds the per-field details; on success the original object passes through unchanged.
await userModel.insert({ name: "Alice", age: 30 }, { validate: true });
await userModel.update("user-123", { name: "Bob" }, { validate: true });
insert validates every annotated codec field. update is partial: only fields present on the patch object are checked, so omitting fields you don't intend to modify is fine. Use Model.validate(obj, { partial: true }) for the same patch semantics outside of update.
Extend BasicDataModel
Extend the generated model class to add custom query methods:
const UserModelBase = BasicDataModel(User, "users");
class CustomUserModel extends UserModelBase {
async findActive() {
return this.table
.filter((u) => u.key("status").eq("active"))
.then((rows) => rows.map(CustomUserModel.fromDatabase) as User[]);
}
async register(email: string, name: string) {
const existing = await this.getBy("email", email);
if (existing.length > 0) {
throw new Error("Email already in use");
}
return this.insert({ email, name, status: "active" });
}
}
GetModel
The GetModel function retrieves or creates a cached model instance for a given schema:
import { GetModel } from "@antelopejs/interface-database-decorators";
const userModel = GetModel(UserModel, "instance-id");
Model instances are cached by class and instance ID, so repeated calls with the same arguments return the same instance. The schema is resolved automatically from the schemaName property on the model class.
Modifier Integration
Data models automatically apply all modifier transformations:
- On insert -
fromPlainDataconversion is applied, theinsertevent fires, and the result is converted withtoDatabasebefore writing. - On update - The same conversion pipeline applies with the
updateevent. - On read - Results from
get,getBy, andgetAllare converted withfromDatabase.
This means encrypted fields are encrypted before storage and decrypted on retrieval, hashed fields are hashed on write, and localized fields follow their configured locale behavior -- all transparently.
Table Modifiers
Table modifiers transform field values as they are stored in and retrieved from the database. They enable transparent encryption, hashing, localization, and custom data transformations. Add modifiers to a table class using the Table.with() method.
Parameter Decoration
The Database Decorators package provides the Model decorator for injecting data model instances into API controller methods. This decorator integrates with the @antelopejs/interface-api package's parameter provider system.