Skip to main content

Entities

Entities are classes that map to database tables.

@Entity() Is the Common Base​

Use the @Entity() decorator in both ORM styles:

  • Active Record: entity extends BaseEntity
  • Repository: entity can be a plain class

@Entity() is what registers the class in the ORM. Extending BaseEntity is only about adding the Active Record API.

Two Entity Shapes​

1. Active Record entity​

Use this shape when the entity itself should expose persistence methods.

import { Entity, PrimaryKey, Property, BaseEntity } from '@carno.js/orm';

@Entity()
export class User extends BaseEntity {
@PrimaryKey({ autoIncrement: true })
id: number;

@Property()
name: string;

@Property({ unique: true })
email: string;
}

This gives you:

  • User.find(), User.findOne(), User.create()
  • user.save(), user.remove(), user.isPersisted()
  • dirty tracking for instance updates

2. Repository-oriented entity​

Use this shape when persistence should stay outside the entity class.

import { Entity, PrimaryKey, Property } from '@carno.js/orm';

@Entity()
export class User {
@PrimaryKey({ autoIncrement: true })
id: number;

@Property()
name: string;

@Property({ unique: true })
email: string;
}

This does not give you:

  • User.find()
  • user.save()
  • user.remove()
  • dirty tracking

Use a Repository<User> for database access in this model.

Defining an Entity​

Use the @Entity() decorator. By default, the table name is derived from the class name (snake_case). If you want Active Record methods, extend BaseEntity. If you want Repository-only usage, a plain class is enough.

import { Entity, PrimaryKey, Property, BaseEntity } from '@carno.js/orm';

@Entity()
export class User extends BaseEntity {
@PrimaryKey({ autoIncrement: true })
id: number;

@Property()
name: string;

@Property({ unique: true })
email: string;

@Property({ default: true })
isActive: boolean;
}

If you want to customize the table name, pass tableName to @Entity():

import { Entity, PrimaryKey, Property } from '@carno.js/orm';

@Entity({ tableName: 'app_user' })
export class User {
@PrimaryKey()
id: number;

@Property()
username: string;
}

Primary Keys​

Use @PrimaryKey() to define the entity identifier. Internally it is a shorthand for @Property({ isPrimary: true }), but @PrimaryKey() is the preferred and clearer form in entity definitions.

Auto-increment Primary Key​

import { Entity, PrimaryKey, Property } from '@carno.js/orm';

@Entity()
export class User {
@PrimaryKey({ autoIncrement: true })
id: number;

@Property()
name: string;
}

Custom Primary Key​

@PrimaryKey() also supports string or UUID-style keys and custom database column names.

import { Entity, PrimaryKey, Property } from '@carno.js/orm';

@Entity()
export class Product {
@PrimaryKey({ columnName: 'product_uuid', dbType: 'uuid' })
productUuid: string;

@Property()
name: string;
}

Property Options​

The @Property() decorator accepts options to define column behavior.

OptionTypeDescription
isPrimarybooleanMark as primary key.
autoIncrementbooleanAuto-incrementing value.
uniquebooleanAdd unique constraint.
indexbooleanAdd a single-column index.
nullablebooleanAllow NULL values.
defaultanyDefault value.
columnNamestringCustom DB column name.
dbType'varchar' | 'text' | 'int' | 'bigint' | 'float' | 'double' | 'decimal' | 'date' | 'datetime' | 'time' | 'timestamp' | 'boolean' | 'json' | 'jsonb' | 'enum' | 'array' | 'uuid'Explicit DB type.
lengthnumberColumn length, usually for string columns.
precisionnumberTotal digits for decimal/numeric values.
scalenumberDecimal digits for decimal/numeric values.
hiddenbooleanHide the property from serialization output.
arraybooleanMark the column as an array type.
isEnumbooleanMark the property as an enum column. Usually handled by @Enum().
enumItemsstring[] | number[] | '__AUTO_DETECT__'Explicit enum values. Usually handled by @Enum().
onInsert() => anyCompute a value before insert.
onUpdate() => anyCompute a value before update.

columnName defaults to the property name converted to snake_case. For most columns, the ORM also infers the column type from the TypeScript type.

For enum fields, prefer the dedicated @Enum() decorator. For calculated non-persisted fields, see @Computed().

Concurrency and Multi-Tenancy Decorators​

Beyond the structural property options, two additional decorators extend how the ORM behaves at runtime.

@Version() — Optimistic Locking​

Place @Version() alongside @Property() on an integer field to enable optimistic concurrency control. The ORM will automatically verify the version on every UPDATE and refuse to apply a stale change — throwing OptimisticLockError instead of silently overwriting newer data.

import { Version } from '@carno.js/orm';

@Entity()
export class Order extends BaseEntity {
@PrimaryKey()
id: number;

@Property()
status: string;

@Property({ default: 0 })
@Version()
version: number;
}

See Optimistic Locking for a full explanation of the conflict detection strategy, how to handle OptimisticLockError, and retry patterns.

@Tenant() — Row-Level Multi-Tenancy​

Place @Tenant() alongside @Property() on the column that identifies which tenant a row belongs to. Once an active tenant is set via tenantContext.run(), the ORM injects the appropriate WHERE tenant_id = ? condition into every query automatically — SELECTs, UPDATEs, DELETEs, and COUNTs alike.

import { Tenant } from '@carno.js/orm';

@Entity()
export class Invoice extends BaseEntity {
@PrimaryKey()
id: number;

@Property()
amount: number;

@Property({ columnName: 'tenant_id' })
@Tenant()
tenantId: number;
}

See Tenant Isolation for full documentation on setting up per-request tenant scoping via middleware, how each query type is affected, and how to handle cross-tenant administrative queries.

Indexes and Unique Constraints​

You can define indexes and unique constraints in three ways: via the @Property shorthand, property decorators, or class decorators (for composite keys).

Shorthand​

For simple, single-column indexes, use the options in @Property.

@Entity()
export class User {
@Property({ unique: true })
email: string;

@Property({ index: true })
status: string;
}

Property Decorators​

You can use specific decorators on the properties.

import { Entity, Property, Index, Unique } from '@carno.js/orm';

@Entity()
export class User {
@Index()
@Property()
createdAt: Date;

@Unique()
@Property()
username: string;
}

Composite (Multi-Column)​

To define indexes or unique constraints that span multiple columns, apply the decorator to the class. These decorators are type-safe and accept a list of property names (keyof T).

import { Entity, Property, Index, Unique } from '@carno.js/orm';

@Entity()
@Unique(['email', 'organizationId']) // Composite Unique
@Index({ properties: ['lastName', 'firstName'] }) // Composite Index
export class User {
@Property()
email: string;

@Property()
organizationId: number;

@Property()
firstName: string;

@Property()
lastName: string;
}

Partial Indexes (Where Clause)​

You can create partial indexes by specifying a where condition. This follows the same syntax as find queries, allowing you to use complex filters and operators. For more details on supported operators, see Querying & Operators.

@Entity()
@Index({
properties: ['email'],
where: { isActive: true } // Only index active users
})
export class User {
// ...
}

Nested Relationship Filtering​

You can filter entities based on properties of their relations. The ORM handles the necessary joins automatically. For detailed information on how this works, see the Relations documentation.