Kinotic
Reference

Decorators Reference

Complete reference for all Kinotic decorators.

Entity Decorators

These decorators are imported from @kinotic-ai/persistence.

@Entity(multiTenancyType?, entityType?)

Marks a class as a Kinotic entity. Required for any class that should be persisted and managed by the entity service system.

import { Entity, MultiTenancyType, EntityType } from '@kinotic-ai/persistence'

@Entity()
export class Product {
    // ...
}

@Entity(MultiTenancyType.SHARED)
export class TenantProduct {
    // ...
}

@Entity(MultiTenancyType.NONE, EntityType.STREAM)
export class SensorReading {
    // ...
}

Parameters:

  • multiTenancyType — MultiTenancyType.NONE (default) or MultiTenancyType.SHARED
  • entityType — EntityType.TABLE (default) or EntityType.STREAM

ID Decorators

@AutoGeneratedId

Marks a field as a server-generated ID. The field type must be string | null since the ID is null before the entity is saved.

import { Entity, AutoGeneratedId } from '@kinotic-ai/persistence'

@Entity()
export class Product {
    @AutoGeneratedId
    id: string | null = null
}

@Id

Marks a field as a manually-assigned ID. The field type is string since the caller is responsible for providing the value.

import { Entity, Id } from '@kinotic-ai/persistence'

@Entity()
export class Product {
    @Id
    sku: string = ''
}

Field Decorators

@NotNull

Marks a field as required. The persistence layer will reject entities where this field is missing or null.

import { Entity, AutoGeneratedId, NotNull } from '@kinotic-ai/persistence'

@Entity()
export class Product {
    @AutoGeneratedId
    id: string | null = null

    @NotNull
    name: string = ''
}

@Text

Enables full-text search indexing on a string field. By default, string fields are indexed as keywords (exact match only). Adding @Text enables tokenized search.

import { Entity, AutoGeneratedId, Text } from '@kinotic-ai/persistence'

@Entity()
export class Article {
    @AutoGeneratedId
    id: string | null = null

    @Text
    body: string = ''
}

@DateTime

Stores a string field as a date, so it can be sorted and range-queried by time. Entities travel as JSON, where a date is an ISO-8601 string, so a date field is a string (or a string[] for a list of dates) holding values such as Date.prototype.toISOString() returns. Applying it to a field of any other type is a compile error, and kinotic generate rejects a field typed Date.

import { Entity, AutoGeneratedId, DateTime } from '@kinotic-ai/persistence'

@Entity()
export class Invoice {
    @AutoGeneratedId
    id: string | null = null

    @DateTime
    issuedAt: string = new Date().toISOString()

    @DateTime
    paidAt: string | null = null
}

@Precision(PrecisionType)

Sets the numeric precision for a number field. Controls how the value is stored in Elasticsearch.

import { Entity, AutoGeneratedId, Precision, PrecisionType } from '@kinotic-ai/persistence'

@Entity()
export class Measurement {
    @AutoGeneratedId
    id: string | null = null

    @Precision(PrecisionType.DOUBLE)
    value: number = 0

    @Precision(PrecisionType.LONG)
    timestamp: number = 0
}

Precision types: PrecisionType.INT, PrecisionType.SHORT, PrecisionType.LONG, PrecisionType.FLOAT, PrecisionType.DOUBLE

@NotIndexed

Excludes a field from Elasticsearch indexing. The field is still stored and returned in queries, but cannot be searched or filtered on.

import { Entity, AutoGeneratedId, NotIndexed } from '@kinotic-ai/persistence'

@Entity()
export class Document {
    @AutoGeneratedId
    id: string | null = null

    @NotIndexed
    rawData: string = ''
}

@Flattened

Uses Elasticsearch flattened mapping for an object field. All values in the object are indexed as keywords, enabling filtering without the overhead of dynamic field mappings.

import { Entity, AutoGeneratedId, Flattened } from '@kinotic-ai/persistence'

@Entity()
export class Event {
    @AutoGeneratedId
    id: string | null = null

    @Flattened
    metadata: Record<string, string> = {}
}

@Nested

Uses Elasticsearch nested mapping for an array of objects. Preserves the relationship between fields within each object, enabling accurate queries across object boundaries.

import { Entity, AutoGeneratedId, Nested } from '@kinotic-ai/persistence'

@Entity()
export class Order {
    @AutoGeneratedId
    id: string | null = null

    @Nested
    lineItems: LineItem[] = []
}

Relationship

@Discriminator(propertyName)

Marks a field as a polymorphic type discriminator. Used when a field can contain different types and the system needs to determine the concrete type during deserialization.

import { Entity, AutoGeneratedId, Discriminator } from '@kinotic-ai/persistence'

@Entity()
export class Notification {
    @AutoGeneratedId
    id: string | null = null

    @Discriminator('type')
    payload: EmailPayload | SmsPayload | null = null
}

Parameters:

  • propertyName — The name of the property within the object that identifies its type

Multi-tenancy

@TenantId

Marks a field as the tenant identifier. Used with MultiTenancyType.SHARED entities to partition data by tenant.

import { Entity, AutoGeneratedId, TenantId, MultiTenancyType } from '@kinotic-ai/persistence'

@Entity(MultiTenancyType.SHARED)
export class Invoice {
    @AutoGeneratedId
    id: string | null = null

    @TenantId
    tenantId: string = ''
}

Versioning

@Version

Enables optimistic locking for the entity. The field type must be string | null. The persistence layer uses this field to detect concurrent modifications.

import { Entity, AutoGeneratedId, Version } from '@kinotic-ai/persistence'

@Entity()
export class Product {
    @AutoGeneratedId
    id: string | null = null

    @Version
    version: string | null = null

    name: string = ''
}

Time Series

@TimeReference

Marks a field as the time reference for stream entities. Used with EntityType.STREAM to identify the timestamp field. Pair it with @DateTime so the field is stored as a date.

import { Entity, AutoGeneratedId, TimeReference, DateTime, EntityType, MultiTenancyType } from '@kinotic-ai/persistence'

@Entity(MultiTenancyType.NONE, EntityType.STREAM)
export class SensorReading {
    @AutoGeneratedId
    id: string | null = null

    @TimeReference
    @DateTime
    timestamp: string = new Date().toISOString()

    value: number = 0
}

Query

@Query(statement)

Method decorator that defines a named query on a repository class. The statement is a SELECT, INSERT, UPDATE or DELETE of the repository's entity in the Kinotic SQL grammar, including an aggregate SELECT (COUNT, SUM, AVG, MIN, MAX, …), with :paramName parameter binding. The method body is left empty -- kinotic generate fills in the implementation. Declare the method on the repository subclass that extends the generated base class (the subclass is generated once and never overwritten).

import { Query } from '@kinotic-ai/persistence'

export class ProductRepository extends BaseProductRepository {

    @Query('SELECT * FROM Product WHERE category = :category ORDER BY name')
    findByCategory(category: string): Promise<Product[]> {
        // Implementation is generated by the CLI
    }

    @Query('SELECT COUNT(*) AS count FROM Product WHERE category = :category')
    countByCategory(category: string): Promise<CategoryCount[]> {
        // Implementation is generated by the CLI
    }
}

Service Decorators

These decorators are imported from @kinotic-ai/core.

@Publish(namespace?, name?, advertise?)

Publishes a class as a remotely accessible service. The service becomes available to clients via the RPC gateway, addressed inside the client's zone (see CRI Format).

import { Publish } from '@kinotic-ai/core'

@Publish('com.example')
class UserService {
    async findUser(id: string): Promise<User> {
        // ...
    }
}

@Publish('com.example', 'CustomName')
class MyService {
    // Published as com.example.CustomName
}

@Publish('com.example', undefined, true)
class OrderService {
    // Also advertised in the service directory
}

Parameters:

  • namespace — Optional service namespace (e.g., 'com.example')
  • name — Optional custom service name. Defaults to the class name.
  • advertise — When true, the service advertises itself in the platform service directory, so it appears in directory listings for browsing and invocation. Defaults to false: the service is callable over RPC but not listed.

@Zone(zone)

Declares the zone a service is addressable in, relative to the client's Kinotic.zonePrefix. When absent, Kinotic.defaultZone (typically loaded from the project package.json kinotic.zone field) applies. The zone is one or more dot-separated labels of lowercase letters, digits, and interior dashes.

import { Publish, Zone } from '@kinotic-ai/core'

@Zone('billing')
@Publish()
class InvoiceService {
    // With zonePrefix app.acme-org.orders-app → srv://app.acme-org.orders-app.billing~InvoiceService
}

@Version(version)

Sets the semantic version for a published service. Enables versioned service routing.

import { Publish, Version } from '@kinotic-ai/core'

@Publish('com.example')
@Version('2.0.0')
class UserService {
    // ...
}

@Scope

Marks the getter or method that provides the scope for service routing. Scope targets requests at one specific instance of a service, such as the copy running on a particular node.

import { Publish, Scope } from '@kinotic-ai/core'

@Publish('com.example')
class NodeManager {
    nodeId: string = ''

    @Scope
    get scope(): string {
        return this.nodeId
    }
}

@Context

Marks a method that receives the request context: a ServiceContext whose participant is the calling participant, as the gateway authenticated it, plus whatever the registered ContextInterceptor adds. This is the TypeScript counterpart of a Java service's Participant parameter.

The context parameter must be the method's final parameter. Callers never pass it — the platform appends it after the caller-supplied arguments, so a context parameter anywhere else will receive a caller argument instead.

import { Publish, Context, type ServiceContext } from '@kinotic-ai/core'

@Publish('com.example')
class AuditService {
    @Context
    async logAction(action: string, context: ServiceContext): Promise<void> {
        console.log(`${context.participant?.id} performed ${action}`)
    }
}

participant is absent only for an invocation that originated inside the platform with no participant bound, such as a scheduled job. See Publishing Services for narrowing it to a scope-typed participant.

Copyright © 2026