dyna-record
    Preparing search index...

    Class defaultAbstract

    Serves as an abstract base class for entities in the ORM system. It defines standard fields such as id, type, createdAt, and updatedAt, and provides static methods for CRUD operations and queries. This class encapsulates common behaviors and properties that all entities share, leveraging decorators for attribute metadata and supporting operations like finding, creating, updating, and deleting entities.

    Table classes should extend this class, and each entity should extend the table class — either directly or through intermediate abstract base classes that hold shared attributes

    Entities extending DynaRecord can utilize these operations to interact with their corresponding records in the database, including handling relationships between different entities.

    @Table({ name: "my-table" })
    abstract class MyTable extends DynaRecord {
    @PartitionKeyAttribute()
    public readonly pk: PartitionKey;

    @SortKeyAttribute()
    public readonly sk: SortKey;
    }

    @Entity
    class User extends MyTable {
    declare readonly type: "User";
    // User implementation
    }

    Implements

    Index

    Constructors

    Properties

    createdAt: Date

    The timestamp marking when the entity was created

    id: string

    A unique identifier for the entity itself, automatically generated upon creation.

    type: string

    The type of the entity. Set automatically to the class name at runtime.

    Each entity must narrow this field to a string literal matching the class name via declare readonly type: "ClassName". This enables compile-time type safety for query filters and return type narrowing.

    updatedAt: Date

    The timestamp marking the last update to the entity. Initially set to the same value as createdAt.

    Methods

    • Get the partition key for an entity

      Returns string

      The partition key of the entity

    • Same as the static update method but on an instance. Returns the full updated instance.

      For @ObjectAttribute fields, the returned instance deep merges the partial update with the existing object value — omitted fields are preserved, and fields set to null are removed, including a nullable field inside a list element.

      Takes the same options as the static method, including a condition; see UpdateOperationOptions.

      Type Parameters

      Parameters

      Returns Promise<EntityInstance<T>>

      The updated instance

      ValidationError Before anything is written, when the attributes do not match the entity's schema.

      FilterError Before anything is read or written, when the write condition is invalid.

      TransactionWriteFailedError When DynamoDB cancels the transaction; a failed condition is reported inside it as a WriteConditionFailedError. See the static update.

      const updated = await customer.update({ name: "Jane Smith" });
      
      const updated = await customer.update({ phone: null });
      // updated.phone is undefined
      // store.address.city is "Springfield"
      const updated = await store.update({ address: { street: "456 Oak Ave" } });
      // updated.address.street is "456 Oak Ave"; updated.address.city is still "Springfield"
      const updated = await order.update(
      { customerId: "customer-2" },
      { referentialIntegrityCheck: false }
      );
      const cancelled = await order.update(
      { status: "cancelled" },
      { condition: { status: "pending" } }
      );
    • Create an entity. If foreign keys are included in the attributes then links will be denormalized accordingly

      The entity, its denormalized copies and its relationship links are written in one transaction. With a condition, the create also guards the rows the new entity references, its BelongsTo parents and the rows its other typed foreign keys reference, and happens only if every guard holds. A create takes no condition on the new row's own attributes, which must not exist yet. See CreateCondition.

      Type Parameters

      Parameters

      • this: EntityClass<T>
      • attributes: CreateOptions<T>

        Attributes of the model to create. A nullable attribute is omitted rather than set to null, inside a list element as anywhere else.

      • Optionaloptions: CreateOperationOptions<T>

        Optional operation options: the referentialIntegrityCheck flag and a write condition on the rows the new entity references, checked in the same transaction as the create. See CreateOperationOptions

      Returns Promise<Promise<EntityAttributesOnly<T>>>

      The new Entity

      ValidationError Before anything is written, when the attributes do not match the entity's schema.

      FilterError Before anything is read or written, when the write condition is invalid, such as a guard on a foreign key the attributes leave unset.

      TransactionWriteFailedError When DynamoDB cancels the transaction. Its errors hold a WriteConditionFailedError for each guarded row whose condition failed, and a ConditionalCheckFailedError for a library check that failed: the entity already exists, or a referenced entity does not exist.

      const customer = await Customer.create({ name: "Jane Doe", status: "active" });
      
      // Denormalizes the Order into its Customer's partition
      const order = await Order.create({
      orderDate: new Date("2026-10-06"),
      total: 40,
      status: "pending",
      customerId: "customer-1",
      storeId: "store-1"
      });
      const order = await Order.create(
      {
      orderDate: new Date("2026-10-06"),
      total: 40,
      status: "pending",
      customerId: "customer-1",
      storeId: "store-1"
      },
      { referentialIntegrityCheck: false }
      );
      // Place an Order only for an active Customer at an open Store
      const order = await Order.create(
      {
      orderDate: new Date("2026-10-06"),
      total: 40,
      status: "pending",
      customerId: "customer-1",
      storeId: "store-1"
      },
      {
      condition: {
      customer: { status: "active" },
      storeId: { target: { status: "open" } }
      }
      }
      );
    • Delete an entity by ID

      • Delete all denormalized records
      • Disassociate all foreign keys of linked models

      With a condition, the entity, its denormalized records and its relationship links are deleted, and its children's foreign keys cleared, only if every part of the condition holds and the entity's own row still exists, checked in the same transaction. See WriteCondition.

      Type Parameters

      Parameters

      • this: EntityClass<T>
      • id: string

        The id of the entity to delete

      • Optionaloptions: DeleteOperationOptions<T>

        Optional operation options: a write condition, checked in the same transaction as the delete. The entity is deleted only if every part of the condition holds and its own row still exists. See DeleteOperationOptions

      Returns Promise<void>

      NotFoundError When the entity does not exist.

      FilterError Before anything is read or written, when the write condition is invalid.

      TransactionWriteFailedError When a child's non-nullable foreign key would be cleared (holding a NullConstraintViolationError, before anything is sent), or when DynamoDB cancels the transaction. Its errors then hold a WriteConditionFailedError for each guarded row whose condition failed, and a ConditionalCheckFailedError for a library check that failed, such as the entity's row being deleted by a concurrent write.

      await Order.delete("order-1");
      
      // Delete an Order only while it is still pending and its Customer is active
      await Order.delete("order-1", {
      condition: { status: "pending", customer: { status: "active" } }
      });
    • Find an entity by Id and optionally include associations.

      Type Parameters

      Parameters

      • this: EntityClass<T>
      • id: string

        Entity Id.

      • Optionaloptions: undefined

        No options provided, returns the entity without included associations.

      Returns Promise<Optional<EntityInstance<T>>>

      An entity without included associations serialized.

      const user = await User.findById("userId");
      

      const user = await User.findById("userId", { include: [{ association: "profile" }] });
      
    • Find an entity by Id and optionally include associations.

      Type Parameters

      Parameters

      Returns Promise<Optional<FindByIdIncludesRes<T, Inc>>>

      An entity without included associations serialized.

      const user = await User.findById("userId");
      

      const user = await User.findById("userId", { include: [{ association: "profile" }] });
      
    • Returns serialized table metadata containing only serializable values. This method returns a plain object representation of the table metadata, with functions, class instances, and other non-serializable data converted to their string representations or omitted. Vector index definitions are included with the model descriptor's name only — never the provider value, client config, or credentials.

      Returns {
          defaultAttributes: Record<
              string,
              | {
                  alias: string;
                  kind: "enum";
                  name: string;
                  nullable: boolean;
                  values: string[];
              }
              | {
                  alias: string;
                  kind: "object";
                  name: string;
                  nullable: boolean;
                  schema: ObjectSchema;
              }
              | {
                  alias: string;
                  foreignKeyTarget: string;
                  kind: "foreignKey";
                  name: string;
                  nullable: boolean;
              }
              | { alias: string; kind: "string"; name: string; nullable: boolean }
              | { alias: string; kind: "number"; name: string; nullable: boolean }
              | { alias: string; kind: "boolean"; name: string; nullable: boolean }
              | { alias: string; kind: "date"; name: string; nullable: boolean },
          >;
          defaultTableAttributes: Record<
              string,
              | {
                  alias: string;
                  kind: "enum";
                  name: string;
                  nullable: boolean;
                  values: string[];
              }
              | {
                  alias: string;
                  kind: "object";
                  name: string;
                  nullable: boolean;
                  schema: ObjectSchema;
              }
              | {
                  alias: string;
                  foreignKeyTarget: string;
                  kind: "foreignKey";
                  name: string;
                  nullable: boolean;
              }
              | { alias: string; kind: "string"; name: string; nullable: boolean }
              | { alias: string; kind: "number"; name: string; nullable: boolean }
              | { alias: string; kind: "boolean"; name: string; nullable: boolean }
              | { alias: string; kind: "date"; name: string; nullable: boolean },
          >;
          delimiter: string;
          entities: Record<
              string,
              {
                  attributes: Record<
                      string,
                      | {
                          alias: string;
                          kind: "enum";
                          name: string;
                          nullable: boolean;
                          values: string[];
                      }
                      | {
                          alias: string;
                          kind: "object";
                          name: string;
                          nullable: boolean;
                          schema: ObjectSchema;
                      }
                      | {
                          alias: string;
                          foreignKeyTarget: string;
                          kind: "foreignKey";
                          name: string;
                          nullable: boolean;
                      }
                      | { alias: string; kind: "string"; name: string; nullable: boolean }
                      | { alias: string; kind: "number"; name: string; nullable: boolean }
                      | { alias: string; kind: "boolean"; name: string; nullable: boolean }
                      | { alias: string; kind: "date"; name: string; nullable: boolean },
                  >;
                  idField?: string;
                  relationships: Record<
                      string,
                      {
                          foreignKey?: string;
                          joinTableName?: string;
                          propertyName: string;
                          target?: string;
                          type: string;
                          uniDirectional?: boolean;
                      },
                  >;
                  tableAttributes: Record<
                      string,
                      | {
                          alias: string;
                          kind: "enum";
                          name: string;
                          nullable: boolean;
                          values: string[];
                      }
                      | {
                          alias: string;
                          kind: "object";
                          name: string;
                          nullable: boolean;
                          schema: ObjectSchema;
                      }
                      | {
                          alias: string;
                          foreignKeyTarget: string;
                          kind: "foreignKey";
                          name: string;
                          nullable: boolean;
                      }
                      | { alias: string; kind: "string"; name: string; nullable: boolean }
                      | { alias: string; kind: "number"; name: string; nullable: boolean }
                      | { alias: string; kind: "boolean"; name: string; nullable: boolean }
                      | { alias: string; kind: "date"; name: string; nullable: boolean },
                  >;
                  tableClassName: string;
              },
          >;
          name: string;
          partitionKeyAttribute: | {
              alias: string;
              kind: "enum";
              name: string;
              nullable: boolean;
              values: string[];
          }
          | {
              alias: string;
              kind: "object";
              name: string;
              nullable: boolean;
              schema: ObjectSchema;
          }
          | {
              alias: string;
              foreignKeyTarget: string;
              kind: "foreignKey";
              name: string;
              nullable: boolean;
          }
          | { alias: string; kind: "string"; name: string; nullable: boolean }
          | { alias: string; kind: "number"; name: string; nullable: boolean }
          | { alias: string; kind: "boolean"; name: string; nullable: boolean }
          | { alias: string; kind: "date"; name: string; nullable: boolean };
          sortKeyAttribute:
              | {
                  alias: string;
                  kind: "enum";
                  name: string;
                  nullable: boolean;
                  values: string[];
              }
              | {
                  alias: string;
                  kind: "object";
                  name: string;
                  nullable: boolean;
                  schema: ObjectSchema;
              }
              | {
                  alias: string;
                  foreignKeyTarget: string;
                  kind: "foreignKey";
                  name: string;
                  nullable: boolean;
              }
              | { alias: string; kind: "string"; name: string; nullable: boolean }
              | { alias: string; kind: "number"; name: string; nullable: boolean }
              | { alias: string; kind: "boolean"; name: string; nullable: boolean }
              | { alias: string; kind: "date"; name: string; nullable: boolean };
          vectorIndexes?: {
              dimensions: number;
              distanceFunction: VectorDistanceFunction;
              fingerprint: string;
              model: string;
              name: string;
              projection: "ALL";
              scopedBy?: string;
              searchSchema: { hash?: string; inlineFilters: string[] };
              vectorAttribute: VectorAttributeName;
          }[];
      }

      A plain object representation of the table metadata

      const metadata = User.metadata();
      // Returns a serialized object with all metadata information
    • Constructs the partition key value

      Parameters

      • id: string

        Entity Id

      Returns string

      Constructed partition key value

      const pkValue = User.partitionKeyValue("userId");
      
    • Query an EntityPartition by EntityId (string) or by PartitionKey/SortKey conditions (object). QueryByIndex not supported with this overload. Use Query with keys and indexName option if needed.

      Filter key validation: Filter keys are strongly typed to only accept valid attribute names from the entity and its declared relationships (@HasMany, @HasOne, @BelongsTo, @HasAndBelongsToMany). The type field only accepts the entity itself or its related entity names — entities from other tables or unrelated entities are rejected. When type is specified as a single value in a $or block, filter keys are narrowed to that entity's attributes.

      Sort key validation: Both skCondition (string form) and sk (object key form) only accept the entity itself or its related entity names, matching dyna-record's single-table sort key format where SK values always start with an entity class name. Unrelated entities and entities from other tables are rejected at compile time.

      SK-scoped filter: When skCondition narrows to specific entities, the filter parameter is scoped to only those entities' attributes. For example, skCondition: { $beginsWith: "Order" } restricts the filter to Order's attributes — using attributes from other entities (e.g., lastFour from PaymentMethod) produces a compile error.

      $beginsWith prefix matching: $beginsWith accepts partial entity name prefixes that match multiple entity types. For example, $beginsWith: "Inv" matches both "Invoice" and "Inventory". The return type and filter are scoped to the union of all matching entities.

      Return type narrowing: The return type narrows automatically based on:

      • Top-level filter type: type: "Order" → Array<EntityAttributesInstance<Order>>
      • Top-level filter type array: type: ["Order", "PaymentMethod"] → union of both
      • Top-level filter keys: { orderDate: "2023" } → narrows to entities that have orderDate
      • $or elements: each block narrows by type (if present) or by filter keys; return type is the union
      • AND intersection: when top-level conditions and $or both narrow, the return type is their intersection. Empty intersection → never[].
      • skCondition option: always intersected with filter narrowing. skCondition: "Order" or { $beginsWith: "Order" } → narrows to Order. { $beginsWith: "Inv" } → narrows to all entities starting with "Inv".
      • No type/keys/SK specified → union of T and all related entities (e.g., Customer | Order | PaymentMethod | ContactInformation)

      Note: When using the object key form ({ pk: "...", sk: "Order" }), the sk value is validated against entity names but does not narrow the return type due to a TypeScript inference limitation. Use filter: { type: "Order" } or the skCondition option for return type narrowing.

      Sort key narrowing reads the entity name: skCondition narrows on an entity name or a prefix of one. "Order" and { $beginsWith: "Order" } narrow to every entity whose name starts with Order (an OrderItem too, if the partition has one). A value that runs past the entity name, such as "Order#123" or { $beginsWith: "Order#" }, is accepted but does not narrow: the table's delimiter is configurable and invisible to the types, so they cannot tell where the name ends. Narrowing assumes no entity's sort key starts with another entity's name followed by the delimiter, which always holds with the default # delimiter and with any delimiter whose first character cannot appear in a class name.

      Reusing a filter: narrowing reads the filter's literal type, which a filter written at the call has. To define a filter once and keep narrowing, check it with satisfies TypedFilterParams<T>. A filter whose type is a TypedFilterParams annotation (a function parameter, an object property) is accepted, but the annotation widens it, so the results are the whole partition's union. Beside an skCondition that names an entity, type a reusable filter as SKScopedFilterParams with the same sort key condition, such as SKScopedFilterParams<Customer, "Order">: a partition-wide TypedFilterParams<T> is refused there, because it offers other entities' keys and type values that cannot match the rows the sort key selects.

      null in a filter: null is not a filter value on its own. Inside an object compared whole (an equality value, an IN element, or a $contains element of a list of objects), null on a nullable field means "not set": the field is left out of the value sent, which then equals a value stored without it.

      Type Parameters

      Parameters

      • this: EntityClass<T>
      • key: string

        Entity Id (string) or an object with PartitionKey and optional SortKey conditions.

      • Optionaloptions: Omit<QueryOptions, "filter" | "indexName" | "skCondition"> & {
            filter?: SKScopedFilterParams<T, SK>;
        } & {
            filter?: CheckedFilterParams<F, SKScopedFilterParams<T, SK>>;
            skCondition?: SK;
        } & FilterInferenceSite<F>

        QueryOptions. Supports typed filter, consistentRead and skCondition. indexName is not supported.

        • filter

          Typed filter conditions. Keys are validated against partition entity attributes, scoped by skCondition when present. The type field accepts valid entity class names within the SK scope.

        • skCondition

          Sort key condition. Accepts entity names, entity-name-prefixed strings, $beginsWith with exact names or partial prefixes, a single comparison, or a $between range. Narrows the return type and scopes the filter to matched entities.

        • Optionalfilter?: SKScopedFilterParams<T, SK>

          Typed filter conditions. Keys are validated against partition entity attributes, scoped by skCondition when present. The type field accepts valid entity class names within the SK scope.

        • Optionalfilter?: CheckedFilterParams<F, SKScopedFilterParams<T, SK>>

          Typed filter conditions. Keys are validated against partition entity attributes, scoped by skCondition when present. The type field accepts valid entity class names within the SK scope.

        • OptionalskCondition?: SK

          Sort key condition. Accepts entity names, entity-name-prefixed strings, $beginsWith with exact names or partial prefixes, a single comparison, or a $between range. Narrows the return type and scopes the filter to matched entities.

        The filter declaration the non-index query overloads infer their const F from, intersected beside the declaration that checks it (CheckedFilterParams).

        F has to be inferred from a naked F in a union, because only there does TypeScript infer a filter typed as a union — a TypedFilterParams<T> or SKScopedFilterParams<T, SK> variable, parameter or property — as the whole union. Inferred through a mapped type instead, each member of the union becomes a separate candidate and one of them is kept, so the other members, such as the one without type, no longer match and the call has no overload. A literal filter is inferred here as its own literal type, as before.

        Checking against a naked F would let a literal's own type vouch for itself, reopening the excess keys CheckedFilterParams closes. Beside F sits UnknownOnceInferred, which resolves to unknown once F is inferred, so this declaration checks nothing and the filter is held to CheckedFilterParams and the declared filter type alone. It must stay its own object in the intersection: as a member of the same property's type the union would be distributed across the check and F would no longer be naked.

      Returns Promise<InferQueryResults<T, F, SK>>

      A promise resolving to query results. The return type narrows based on the filter's type value, filter keys, and skCondition.

      FilterError Before the query is sent, when a filter or key condition value cannot match: a filter key naming no attribute of the partition's entities, a value that fails the attribute's schema, an inverted $between, a dot path the schema does not declare, a field an object operand's schema does not declare, or a key condition set to undefined.

      const results = await Customer.query("123");
      
      const orders = await Customer.query("123", { skCondition: "Order" });
      // orders is Array<EntityAttributesInstance<Order>>
      const orders = await Customer.query("123", { skCondition: { $beginsWith: "Order" } });
      // orders is Array<EntityAttributesInstance<Order>>
      const results = await Customer.query("123", { skCondition: { $beginsWith: "C" } });
      // results includes Customer and ContactInformation (both start with "C")
      // filter accepts attributes from both entities
      const orders = await Customer.query("123", {
      skCondition: { $beginsWith: "Order" },
      filter: { type: "Order", orderDate: new Date("2023-01-01") }
      });
      // orders is Array<EntityAttributesInstance<Order>>
      // filter: { lastFour: "1234" } would be a compile error (PaymentMethod attribute)
      // Several comparison operators on one attribute compose with AND
      const orders = await Customer.query("123", {
      filter: {
      type: "Order",
      orderDate: { $gte: new Date("2026-01-01"), $lt: new Date("2026-02-01") }
      }
      });

      // $between is inclusive on both bounds
      const results = await Customer.query("123", {
      filter: { orderDate: { $between: [new Date("2026-01-01"), new Date("2026-12-31")] } }
      });
      // A key condition narrows what DynamoDB reads; a filter discards rows
      // after reading them. A sort key takes one condition, so a two-sided
      // range is $between
      const orders = await Customer.query("123", {
      skCondition: { $between: ["Order#100", "Order#200"] }
      });
      const results = await Customer.query({ pk: "Customer#123", sk: "Order" });
      // results is QueryResults<Customer> — use filter type for narrowing
      const orders = await Customer.query(
      { pk: "Customer#123", sk: { $beginsWith: "Order" } },
      { filter: { type: "Order" } }
      );
      // orders is Array<EntityAttributesInstance<Order>>
      const ordersIn2026 = {
      type: "Order",
      orderDate: { $gte: new Date("2026-01-01") }
      } satisfies TypedFilterParams<Customer>;

      const orders = await Customer.query("123", { filter: ordersIn2026 });
      // orders is Array<EntityAttributesInstance<Order>>

      async function customerRecords(filter: TypedFilterParams<Customer>) {
      // Accepted, but the annotation widens the filter: QueryResults<Customer>
      return await Customer.query("123", { filter });
      }
      async function ordersFor(
      customerId: string,
      filter: SKScopedFilterParams<Customer, "Order">
      ) {
      // Array<EntityAttributesInstance<Order>>
      return await Customer.query(customerId, { skCondition: "Order", filter });
      }

      await ordersFor("123", { orderDate: { $gte: new Date("2026-01-01") } });
      const results = await Customer.query("123", {
      skCondition: { $beginsWith: "Order#" }
      });
      // results is QueryResults<Customer> — the types cannot see the delimiter
      const results = await Customer.query("123", { consistentRead: true });
      
    • Query an EntityPartition by EntityId (string) or by PartitionKey/SortKey conditions (object). QueryByIndex not supported with this overload. Use Query with keys and indexName option if needed.

      Filter key validation: Filter keys are strongly typed to only accept valid attribute names from the entity and its declared relationships (@HasMany, @HasOne, @BelongsTo, @HasAndBelongsToMany). The type field only accepts the entity itself or its related entity names — entities from other tables or unrelated entities are rejected. When type is specified as a single value in a $or block, filter keys are narrowed to that entity's attributes.

      Sort key validation: Both skCondition (string form) and sk (object key form) only accept the entity itself or its related entity names, matching dyna-record's single-table sort key format where SK values always start with an entity class name. Unrelated entities and entities from other tables are rejected at compile time.

      SK-scoped filter: When skCondition narrows to specific entities, the filter parameter is scoped to only those entities' attributes. For example, skCondition: { $beginsWith: "Order" } restricts the filter to Order's attributes — using attributes from other entities (e.g., lastFour from PaymentMethod) produces a compile error.

      $beginsWith prefix matching: $beginsWith accepts partial entity name prefixes that match multiple entity types. For example, $beginsWith: "Inv" matches both "Invoice" and "Inventory". The return type and filter are scoped to the union of all matching entities.

      Return type narrowing: The return type narrows automatically based on:

      • Top-level filter type: type: "Order" → Array<EntityAttributesInstance<Order>>
      • Top-level filter type array: type: ["Order", "PaymentMethod"] → union of both
      • Top-level filter keys: { orderDate: "2023" } → narrows to entities that have orderDate
      • $or elements: each block narrows by type (if present) or by filter keys; return type is the union
      • AND intersection: when top-level conditions and $or both narrow, the return type is their intersection. Empty intersection → never[].
      • skCondition option: always intersected with filter narrowing. skCondition: "Order" or { $beginsWith: "Order" } → narrows to Order. { $beginsWith: "Inv" } → narrows to all entities starting with "Inv".
      • No type/keys/SK specified → union of T and all related entities (e.g., Customer | Order | PaymentMethod | ContactInformation)

      Note: When using the object key form ({ pk: "...", sk: "Order" }), the sk value is validated against entity names but does not narrow the return type due to a TypeScript inference limitation. Use filter: { type: "Order" } or the skCondition option for return type narrowing.

      Sort key narrowing reads the entity name: skCondition narrows on an entity name or a prefix of one. "Order" and { $beginsWith: "Order" } narrow to every entity whose name starts with Order (an OrderItem too, if the partition has one). A value that runs past the entity name, such as "Order#123" or { $beginsWith: "Order#" }, is accepted but does not narrow: the table's delimiter is configurable and invisible to the types, so they cannot tell where the name ends. Narrowing assumes no entity's sort key starts with another entity's name followed by the delimiter, which always holds with the default # delimiter and with any delimiter whose first character cannot appear in a class name.

      Reusing a filter: narrowing reads the filter's literal type, which a filter written at the call has. To define a filter once and keep narrowing, check it with satisfies TypedFilterParams<T>. A filter whose type is a TypedFilterParams annotation (a function parameter, an object property) is accepted, but the annotation widens it, so the results are the whole partition's union. Beside an skCondition that names an entity, type a reusable filter as SKScopedFilterParams with the same sort key condition, such as SKScopedFilterParams<Customer, "Order">: a partition-wide TypedFilterParams<T> is refused there, because it offers other entities' keys and type values that cannot match the rows the sort key selects.

      null in a filter: null is not a filter value on its own. Inside an object compared whole (an equality value, an IN element, or a $contains element of a list of objects), null on a nullable field means "not set": the field is left out of the value sent, which then equals a value stored without it.

      Type Parameters

      Parameters

      • this: EntityClass<T>
      • key: EntityKeyConditions<T>

        Entity Id (string) or an object with PartitionKey and optional SortKey conditions.

      • Optionaloptions: Omit<OptionsWithoutIndex<T>, "skCondition"> & {
            filter?: CheckedFilterParams<F, TypedFilterParams<T>>;
        } & FilterInferenceSite<F>

        QueryOptions. Supports typed filter, consistentRead and skCondition. indexName is not supported.

        • filter

          Typed filter conditions. Keys are validated against partition entity attributes, scoped by skCondition when present. The type field accepts valid entity class names within the SK scope.

        • skCondition

          Sort key condition. Accepts entity names, entity-name-prefixed strings, $beginsWith with exact names or partial prefixes, a single comparison, or a $between range. Narrows the return type and scopes the filter to matched entities.

        • Optionalfilter?: CheckedFilterParams<F, TypedFilterParams<T>>

          Typed filter conditions. Keys are validated against partition entity attributes, scoped by skCondition when present. The type field accepts valid entity class names within the SK scope.

        The filter declaration the non-index query overloads infer their const F from, intersected beside the declaration that checks it (CheckedFilterParams).

        F has to be inferred from a naked F in a union, because only there does TypeScript infer a filter typed as a union — a TypedFilterParams<T> or SKScopedFilterParams<T, SK> variable, parameter or property — as the whole union. Inferred through a mapped type instead, each member of the union becomes a separate candidate and one of them is kept, so the other members, such as the one without type, no longer match and the call has no overload. A literal filter is inferred here as its own literal type, as before.

        Checking against a naked F would let a literal's own type vouch for itself, reopening the excess keys CheckedFilterParams closes. Beside F sits UnknownOnceInferred, which resolves to unknown once F is inferred, so this declaration checks nothing and the filter is held to CheckedFilterParams and the declared filter type alone. It must stay its own object in the intersection: as a member of the same property's type the union would be distributed across the check and F would no longer be naked.

      Returns Promise<InferQueryResults<T, F>>

      A promise resolving to query results. The return type narrows based on the filter's type value, filter keys, and skCondition.

      FilterError Before the query is sent, when a filter or key condition value cannot match: a filter key naming no attribute of the partition's entities, a value that fails the attribute's schema, an inverted $between, a dot path the schema does not declare, a field an object operand's schema does not declare, or a key condition set to undefined.

      const results = await Customer.query("123");
      
      const orders = await Customer.query("123", { skCondition: "Order" });
      // orders is Array<EntityAttributesInstance<Order>>
      const orders = await Customer.query("123", { skCondition: { $beginsWith: "Order" } });
      // orders is Array<EntityAttributesInstance<Order>>
      const results = await Customer.query("123", { skCondition: { $beginsWith: "C" } });
      // results includes Customer and ContactInformation (both start with "C")
      // filter accepts attributes from both entities
      const orders = await Customer.query("123", {
      skCondition: { $beginsWith: "Order" },
      filter: { type: "Order", orderDate: new Date("2023-01-01") }
      });
      // orders is Array<EntityAttributesInstance<Order>>
      // filter: { lastFour: "1234" } would be a compile error (PaymentMethod attribute)
      // Several comparison operators on one attribute compose with AND
      const orders = await Customer.query("123", {
      filter: {
      type: "Order",
      orderDate: { $gte: new Date("2026-01-01"), $lt: new Date("2026-02-01") }
      }
      });

      // $between is inclusive on both bounds
      const results = await Customer.query("123", {
      filter: { orderDate: { $between: [new Date("2026-01-01"), new Date("2026-12-31")] } }
      });
      // A key condition narrows what DynamoDB reads; a filter discards rows
      // after reading them. A sort key takes one condition, so a two-sided
      // range is $between
      const orders = await Customer.query("123", {
      skCondition: { $between: ["Order#100", "Order#200"] }
      });
      const results = await Customer.query({ pk: "Customer#123", sk: "Order" });
      // results is QueryResults<Customer> — use filter type for narrowing
      const orders = await Customer.query(
      { pk: "Customer#123", sk: { $beginsWith: "Order" } },
      { filter: { type: "Order" } }
      );
      // orders is Array<EntityAttributesInstance<Order>>
      const ordersIn2026 = {
      type: "Order",
      orderDate: { $gte: new Date("2026-01-01") }
      } satisfies TypedFilterParams<Customer>;

      const orders = await Customer.query("123", { filter: ordersIn2026 });
      // orders is Array<EntityAttributesInstance<Order>>

      async function customerRecords(filter: TypedFilterParams<Customer>) {
      // Accepted, but the annotation widens the filter: QueryResults<Customer>
      return await Customer.query("123", { filter });
      }
      async function ordersFor(
      customerId: string,
      filter: SKScopedFilterParams<Customer, "Order">
      ) {
      // Array<EntityAttributesInstance<Order>>
      return await Customer.query(customerId, { skCondition: "Order", filter });
      }

      await ordersFor("123", { orderDate: { $gte: new Date("2026-01-01") } });
      const results = await Customer.query("123", {
      skCondition: { $beginsWith: "Order#" }
      });
      // results is QueryResults<Customer> — the types cannot see the delimiter
      const results = await Customer.query("123", { consistentRead: true });
      
    • Query by PartitionKey and optional SortKey/Filter/Index conditions with an index When querying on an index, any of the entities attributes can be part of the key condition

      Type Parameters

      Parameters

      • this: EntityClass<T>
      • key: IndexKeyConditions<T>

        Any attribute defined on the entity that is part of an index's keys

      • options: { consistentRead?: undefined; filter?: FilterParams; indexName: string }

        QueryBuilderOptions

      Returns Promise<QueryResults<T>>

       const result = await User.query(
      {
      name: "SomeName" // An attribute that is part of the key condition on an index
      },
      { indexName: "myIndex" }
      );
    • Update an entity. If foreign keys are included in the attributes then:

      • Manages associated relationship links as needed
      • If the entity already had a foreign key relationship, then denormalized records will be deleted from each partition
      • Validation errors will be thrown if the attribute being removed is not nullable

      Setting a nullable attribute to null removes it. The same holds for a nullable field inside an @ObjectAttribute and inside an element of a list, which is written whole and stored without the field.

      With a condition, the entity, its denormalized copies and its relationship links are updated only if every part of the condition holds, checked in the same transaction: a condition on the entity's own row, on its related entities by relationship name, and on the rows its other foreign keys reference through target. See WriteCondition.

      Type Parameters

      Parameters

      Returns Promise<void>

      ValidationError Before anything is written, when the attributes do not match the entity's schema.

      FilterError Before anything is read or written, when the write condition is invalid, such as an undefined operand or a guard on a relationship whose foreign key the same update sets to null.

      NotFoundError When the update reads the entity first (it has relationships, or a condition needs its stored row) and the entity does not exist.

      TransactionWriteFailedError When DynamoDB cancels the transaction. Its errors hold a WriteConditionFailedError for each guarded row whose condition failed, and a ConditionalCheckFailedError for a library check that failed: a missing row, a referenced entity that does not exist, a relationship changed by a concurrent write, or a HasMany or HasAndBelongsToMany id that is not related.

      await Customer.update("customer-1", { name: "Jane Smith", status: "suspended" });
      
      await Customer.update("customer-1", { phone: null });
      
      await Order.update(
      "order-1",
      { customerId: "customer-2" },
      { referentialIntegrityCheck: false }
      );
      await Store.update("store-1", { address: { city: "Springfield" } });
      
      await Product.update("product-1", { description }, { forceEmbed: true });
      
      // Cancel an Order only while it is still pending and its Customer is active
      await Order.update(
      "order-1",
      { status: "cancelled" },
      { condition: { status: "pending", customer: { status: "active" } } }
      );

      // Ship an Order only if it has no tracking number yet: null means "not set"
      await Order.update(
      "order-1",
      { status: "shipped", trackingNumber: "1Z999" },
      { condition: { trackingNumber: null } }
      );
    • Declares a table's complete set of vector indexes in one call, keyed by export name, and returns the typed index constructs — each index's search surface and provisioning definition.

      Every index declares its own vectorAttribute (the physical attribute its members' vectors are written under), its own embedding config, and its complete membership with members: — there is no derived or universal membership. Indexes have fully independent physical membership: each searchable entity belongs to exactly one index and is ingested, billed, and searchable only there.

      A scoped index (scopedBy) makes the scope parent's foreign key the index HASH, and its construct's search takes the scope id first with in: and result unions typed to the membership. An unscoped index omits scopedBy — it has no HASH, its search takes the query first, and every search spans its whole membership.

      Only table classes (classes decorated with @Table) may declare vector indexes; calling this on an entity class throws, as does a second call for the same table. Declaring indexes never triggers metadata initialization and never resolves the entity thunks — index constants can be declared at module evaluation, and membership is resolved and validated when metadata initializes (first operation or an explicit metadata() call).

      Type Parameters

      Parameters

      Returns VectorIndexConstructs<T>

      The registered VectorIndexMetadata constructs, keyed as declared

      const { listingSearchIndex, supportSearchIndex } = MyTable.vectorIndexes({
      listingSearchIndex: {
      name: "listing-search-index",
      vectorAttribute: "__dyna_vector",
      model: TitanTextEmbedV2,
      provider: myEmbedFunction,
      scopedBy: () => Store,
      members: [() => Listing, () => Review]
      },
      supportSearchIndex: {
      name: "support-search-index",
      vectorAttribute: "__dyna_vector_support",
      model: TitanTextEmbedV2,
      provider: myEmbedFunction,
      scopedBy: () => Store,
      members: [() => SupportArticle]
      }
      });