Skip to main content

Model Mixins

with defineModel you can share column definitions by composing plain objects. Spread shared column sets into your model's columns definition for DRY, consistent models.

Sharing Column Definitions

Define reusable column sets as plain objects, then spread them into defineModel:

import { defineModel, defineRelations, createSchema, col } from "hysteria-orm";

// Reusable timestamp columns
const timestampColumns = {
createdAt: col.datetime({ autoCreate: true }),
updatedAt: col.datetime({ autoCreate: true, autoUpdate: true }),
deletedAt: col.datetime(),
};

// Reusable UUID primary key
const uuidPk = {
id: col.uuid({ primaryKey: true }),
};

// Reusable auto-increment primary key
const incrementPk = {
id: col.increment(),
};

const User = defineModel("users", {
columns: {
...uuidPk,
name: col.string(),
email: col.string({ nullable: false }),
...timestampColumns,
},
});

const Post = defineModel("posts", {
columns: {
...incrementPk,
title: col.string(),
body: col.text(),
userId: col.integer(),
...timestampColumns,
},
});

// Relations defined separately — no cross-file circular deps
const PostRelations = defineRelations(Post, ({ belongsTo }) => ({
author: belongsTo(User, { foreignKey: "userId" }),
}));

export const schema = createSchema(
{ users: User, posts: Post },
{ posts: PostRelations },
);

Common Patterns

Primary Key + Timestamps

const baseColumns = {
id: col.increment(),
createdAt: col.datetime({ autoCreate: true }),
updatedAt: col.datetime({ autoCreate: true, autoUpdate: true }),
deletedAt: col.datetime(),
};

const Category = defineModel("categories", {
columns: { ...baseColumns, name: col.string(), slug: col.string() },
});

const Tag = defineModel("tags", {
columns: { ...baseColumns, label: col.string({ nullable: false }) },
});

UUID-based Models

const uuidBase = {
id: col.uuid({ primaryKey: true }),
createdAt: col.datetime({ autoCreate: true }),
updatedAt: col.datetime({ autoCreate: true, autoUpdate: true }),
};

const Product = defineModel("products", {
columns: {
...uuidBase,
name: col.string({ nullable: false }),
price: col.float(),
stock: col.integer(),
},
});

ULID-based Models

const ulidBase = {
id: col.ulid({ primaryKey: true }),
createdAt: col.datetime({ autoCreate: true }),
updatedAt: col.datetime({ autoCreate: true, autoUpdate: true }),
};

const Event = defineModel("events", {
columns: {
...ulidBase,
name: col.string(),
occurredAt: col.datetime(),
},
});

Audit Columns

const auditColumns = {
createdBy: col.string(),
updatedBy: col.string(),
};

const Document = defineModel("documents", {
columns: {
id: col.uuid({ primaryKey: true }),
title: col.string({ nullable: false }),
...timestampColumns,
...auditColumns,
},
});

Choosing a Primary Key Strategy

StrategyColumn DefinitionUse When
Auto-incrementcol.increment()Traditional integer IDs
Big auto-incrementcol.bigIncrement()Tables expected to exceed 2 billion rows
UUIDcol.uuid({ primaryKey: true })Distributed systems, globally unique IDs
ULIDcol.ulid({ primaryKey: true })Sortable unique IDs (ordered by time)

Type Exports

Field interfaces are still exported for use in your own code:

import {
type TimestampFields,
type UuidFields,
type UlidFields,
type IncrementFields,
type BigIntFields,
} from "hysteria-orm";

function processTimestamped<T extends TimestampFields>(record: T) {
console.log(`Created at: ${record.createdAt}`);
console.log(`Updated at: ${record.updatedAt}`);
}

See Also


Next: Model Hooks & Lifecycle