Skip to main content

Defining SQL Models

Models represent tables in your database. Define them by extending the Model class and using decorators for columns and relations.

Decorators & Column Types

Hysteria ORM uses TypeScript decorators to define model fields and relationships. Decorators are special annotations that add metadata or behavior to class properties.

Column Decorators

DecoratorDescriptionExample Usage
@column()Standard column. Accepts options like primaryKey, hidden, etc.@column() name: string;
@column.integer()Ensures value is stored as integer. Useful for PKs and numeric fields.@column.integer({ primaryKey: true }) id: number;
@column.boolean()Stores value as boolean, handles DB-specific formats.@column.boolean() isActive: boolean;
@column.json()Serializes/deserializes JSON objects automatically.@column.json() json: Record<string, any>;
@column.date()Handles date fields, with options for auto-creation and auto-update.@column.date({ autoCreate: true }) createdAt: Date;
@column.uuid()Auto-generates a UUID if not provided.@column.uuid({ primaryKey: true }) uuid: string;
@column.ulid()Auto-generates a ULID if not provided.@column.ulid({ primaryKey: true }) ulid: string;
@column.encryption.symmetric()Encrypts/decrypts value using a symmetric key.@column.encryption.symmetric({ key }) secret: string;
@column.encryption.asymmetric()Encrypts/decrypts value using asymmetric keys.@column.encryption.asymmetric({ publicKey, privateKey }) secret: string;

Column Options

You can pass options to @column() and its variants to control column behavior:

OptionTypeDefaultDescription
primaryKeybooleanfalseMarks this column as the primary key. Only one primary key is allowed per model.
serializefunctionundefinedFunction to transform the value after reading from the database (e.g., parse, convert type).
preparefunctionundefinedFunction to transform the value before writing to the database (e.g., format, encrypt).
hiddenbooleanfalseIf true, this column will not appear in serialized output (e.g., API responses).
autoUpdatebooleanfalseIf true, prepare will always be called on update, even if the value is not in the payload.
databaseNamestringproperty name (case-converted)Custom name for the column in the database.
Example Usage
@column({ primaryKey: true, hidden: true, databaseName: 'user_id' })
declare id: number;

@column({
prepare: (value) => value.trim(),
serialize: (value) => value.toUpperCase(),
})
declare name: string;

Notes:

  • primaryKey: Composite primary keys are not supported. Defining more than one will throw an error.
  • hidden: Useful for sensitive fields like passwords.
  • prepare/serialize: Use for custom transformations, e.g., encryption, formatting, or type conversion.
  • databaseName: Use if your DB column name differs from your property name or case convention.

Examples

@column()
declare name: string;

@column.integer({ primaryKey: true })
declare id: number;

@column.boolean()
declare isActive: boolean;

@column.json()
declare json: Record<string, any> | null;

@column.date({ autoCreate: true })
declare createdAt: Date;

@column.uuid({ primaryKey: true })
declare uuid: string;

@column.ulid({ primaryKey: true })
declare ulid: string;

@column.encryption.symmetric({ key: 'my-secret-key' })
declare secret: string;

Relation Decorators

DecoratorDescriptionExample Usage
@hasOneOne-to-one relationship@hasOne(() => Profile, 'userId') profile: Profile;
@hasManyOne-to-many relationship@hasMany(() => Post, 'userId') posts: Post[];
@belongsToInverse of hasOne/hasMany@belongsTo(() => User, 'userId') user: User;
@manyToManyMany-to-many via a join table@manyToMany(() => Role, () => UserRole, { leftForeignKey: 'userId', rightForeignKey: 'roleId' }) roles: Role[];

Model Static Properties

You can customize model behavior by setting static properties on your model class:

PropertyDescriptionDefaultExample Usage
static _tableCustom table name for the model.Pluralized snake_casestatic _table = "users";
static modelCaseConventionCase convention for model property names (e.g., camel, snake, pascal, none)."camel"static modelCaseConvention = "camel";
static databaseCaseConventionCase convention for database column names."snake"static databaseCaseConvention = "snake";
static softDeleteColumnColumn name used for soft deletes."deletedAt"static softDeleteColumn = "deletedAt";
static softDeleteValueValue set when soft deleting (usually a timestamp).Current UTC datetimeYYYY-MM-DD HH:mm:ss

Details

  • Custom Table Name (_table): By default, the table name is the pluralized, snake_case version of your model class. Override this by setting static _table.

  • Case Conventions:

    • modelCaseConvention: Controls how property names are interpreted in your code (default: camelCase).
    • databaseCaseConvention: Controls how column names are mapped in the database (default: snake_case). This is useful for aligning with your database's naming style.
  • Soft Delete Support:

    • softDeleteColumn: The column used to mark a record as deleted (default: deletedAt).
    • softDeleteValue: The value written to the soft delete column (default: current UTC datetime in "YYYY-MM-DD HH:mm:ss" format). When you soft delete a record, this value is set instead of actually removing the row.

Example: User Model

import { Model, column, hasMany, hasOne, manyToMany } from 'hysteria-orm';
import { Post } from './Post';
import { Address } from './Address';

export class User extends Model {
// Custom table name for this model
static _table = "users";

// Optional: override case conventions if needed
// static modelCaseConvention = "camel";
// static databaseCaseConvention = "snake";

// Optional: customize soft delete behavior
// static softDeleteColumn = "deletedAt";
// static softDeleteValue = new Date().toISOString();

@column.integer({ primaryKey: true })
declare id: number;

@column()
declare name: string;

@column()
declare email: string;

@column({ hidden: true })
declare password: string;

@column()
declare status: 'active' | 'inactive';

@column.boolean()
declare isActive: boolean;

@column.json()
declare json: Record<string, any> | null;

@column.date({ autoCreate: true })
declare createdAt: Date;

@column.date({ autoCreate: true, autoUpdate: true })
declare updatedAt: Date;

@hasOne(() => Post, 'userId')
declare post: Post;

@hasMany(() => Post, 'userId')
declare posts: Post[];

@manyToMany(() => Address, () => UserAddress, {
leftForeignKey: 'userId',
rightForeignKey: 'addressId',
})
declare addresses: Address[];
}

Decorators

  • @column() for fields
  • @column.integer(), @column.boolean(), @column.json(), @column.date() for typed fields
  • @hasOne, @hasMany, @belongsTo, @manyToMany for relations

Next: Model Hooks