Skip to content

Expressions: Condition builder

Eugene Lazutkin edited this page Jul 17, 2026 · 2 revisions

Expressions: Condition builder

buildCondition(clauses, params?) builds a DynamoDB ConditionExpression from a declarative clause tree. Composable with the other expression builders — names and value placeholders get unique aliases that don't collide with buildUpdate / addProjection / buildSearch.

See also (AWS JS SDK v3): Condition expressions · Expression attribute names · PutCommand / UpdateCommand / DeleteCommand. Vocabulary: Concepts.

buildCondition is an additive builder — same contract as Update builder → Additive builders. It appends to whatever ConditionExpression is already on params by AND-joining, so you can call it multiple times, and it interleaves freely with buildUpdate, buildSearch, and addProjection.

import {buildCondition} from 'dynamodb-toolkit/expressions';

const params = buildCondition([
  {path: 'status', op: '=', value: 'active'},
  {op: 'or', clauses: [
    {path: 'priority', op: '>', value: 5},
    {path: 'tags', op: 'contains', value: 'urgent'}
  ]}
]);
// ConditionExpression: "#cd0 = :cdv0 AND (#cd1 > :cdv1 OR contains(#cd2, :cdv2))"

Clause shapes

type ConditionClause =
  | {path: string; op: '=' | '<>' | '<' | '<=' | '>' | '>='; value: unknown}
  | {path: string; op: 'exists' | 'notExists'}
  | {path: string; op: 'beginsWith' | 'contains'; value: unknown}
  | {path: string; op: 'in'; value: unknown[]} // `values` still accepted as a legacy alias
  | {op: 'and' | 'or'; clauses: ConditionClause[]}
  | {op: 'not'; clause: ConditionClause};

Top-level array semantics

The top-level clauses array is AND-joined. To mix OR at the top level, wrap explicitly:

buildCondition([{op: 'or', clauses: [...]}]);

Path semantics

Paths use the same dotted-segment convention as the Update builder — pure-digit segments become array indices, dotted paths nest into maps:

buildCondition([{path: 'config.thresholds.0', op: '>=', value: 10}]);
// ConditionExpression: "#cd0.#cd1[0] >= :cdv0"

Combining with the Adapter

The put / patch / delete methods accept a conditions option that's fed straight to buildCondition:

await adapter.put(item, {
  conditions: [
    {path: 'version', op: '=', value: 3},
    {path: 'locked', op: 'notExists'}
  ]
});

// Guarded delete — see Adapter: CRUD methods → delete
await adapter.delete({name: 'Hoth'}, {
  conditions: [{path: 'status', op: '=', value: 'archived'}]
});

The conditions are added on top of any existing attribute_exists / attribute_not_exists checks the Adapter applies (e.g., for post). See Adapter: CRUD methods for the delete guard pattern and Adapter: Hooks updateInput for the broader "insert a cross-cutting condition" story.

Escape hatch

For one-off conditions the builder doesn't cover — DynamoDB functions like size(), attribute_type(), complex precedence — use the updateInput hook to add the raw expression after the builder runs:

hooks: {
  updateInput(input, op) {
    if (op.name === 'put') {
      input.ConditionExpression = `(${input.ConditionExpression}) AND size(#tags) < :cap`;
      input.ExpressionAttributeNames['#tags'] = 'tags';
      input.ExpressionAttributeValues[':cap'] = 100;
    }
    return input;
  }
}

Clone this wiki locally