Function ObjectAttribute

  • A decorator for marking class fields as structured object attributes within the context of a single-table design entity.

    Objects are stored as native DynamoDB Map types and validated at runtime against the provided schema. The TypeScript type is inferred from the schema using InferObjectSchema.

    Object attributes are never nullable. DynamoDB cannot update nested document paths (e.g. address.geo.lat) if the parent object does not exist, which causes: ValidationException: The document path provided in the update expression is invalid for update. To prevent this, @ObjectAttribute fields must always exist as at least an empty object {}. Similarly, nested object fields within the schema cannot be nullable.

    Supported field types within the schema:

    • "string", "number", "boolean" — primitives (support nullable: true)
    • "enum" — string literal unions (support nullable: true)
    • "date" — dates stored as ISO strings (support nullable: true)
    • "object" — nested objects, arbitrarily deep (never nullable)
    • "array" — lists of any field type (support nullable: true, full replacement on update)

    Objects within arrays are not subject to the document path limitation because arrays use full replacement on update. Partial updates of individual objects within arrays are not supported.

    Type Parameters

    • T extends default

      The class type that the decorator is applied to

    • const S extends ObjectSchema

      The ObjectSchema type used for validation and type inference

    Parameters

    Returns (
        _value: undefined,
        context: AttributeDecoratorContext<
            T,
            InferObjectSchema<S>,
            ObjectAttributeOptions<S>,
        >,
    ) => void

    A class field decorator function

    Usage example:

    const addressSchema = {
    street: { type: "string" },
    city: { type: "string" },
    zip: { type: "number", nullable: true },
    category: { type: "enum", values: ["home", "work", "other"] },
    geo: {
    type: "object",
    fields: {
    lat: { type: "number" },
    lng: { type: "number" },
    accuracy: { type: "enum", values: ["precise", "approximate"] }
    }
    }
    } as const satisfies ObjectSchema;

    class MyEntity extends MyTable {
    @ObjectAttribute({ alias: 'Address', schema: addressSchema })
    public address: InferObjectSchema<typeof addressSchema>;
    }

    // TypeScript infers:
    // address.category → "home" | "work" | "other"
    // address.geo.accuracy → "precise" | "approximate"

    Partial updates: When updating an entity, @ObjectAttribute fields support partial updates — only the fields you provide are modified, omitted fields are preserved. Under the hood, dyna-record generates DynamoDB document path expressions (e.g., SET #address.#street = :address_street) instead of replacing the entire map. Nested objects are recursively merged. Arrays within objects are full replacement. Setting a nullable field within an object to null generates a REMOVE expression for that specific field.

    // Only updates street — city, zip, geo are preserved
    await MyEntity.update("id", { address: { street: "456 Oak Ave" } });

    // Remove a nullable field within the object
    await MyEntity.update("id", { address: { zip: null } });

    Object attributes support filtering in queries using dot-path notation for nested fields and the ContainsFilter | $contains operator for List membership checks.

    await MyEntity.query("123", {
    filter: { "address.city": "Springfield" }
    });

    await MyEntity.query("123", {
    filter: { "address.tags": { $contains: "home" } }
    });