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.