Skip to content

Recipes

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

Recipes

Pattern-first pages for common DynamoDB problems that feel trivial in SQL but need non-obvious shapes here. Audience: developers moving from SQL to DynamoDB — usually because of Lambda cost, cold-start latency, or serverless-native ergonomics — who hit the "how do I even do $X?" wall.

Every recipe opens with the SQL-equivalent question, shows the pattern against real toolkit primitives, tables the cost / capacity tradeoffs, and calls out "when this doesn't fit" honestly. None of them is "the only way" — the closing sections list the alternatives you might reach for instead.

Recipes answer "how do I do X". For the design-level layer — how to shape keys, tables, paging, and URLs in the first place — see the guides: Hierarchical data walkthrough, Key expression patterns, Multi-type tables, Pagination, Mass operation semantics, URL schema design.

Which recipe?

If you want to… Start here
List every record of a "type" across every partition (SELECT * WHERE kind = 'state') List records of a tier
List every record of a type within one partition (SELECT * WHERE state = 'TX' AND kind = 'facility') List records of a tier within a partition
Finer per-tier control — different projections, sharding at scale Per-tier sparse GSI markers
Short-lived holds with auto-expiry (car reservation, meeting-room booking, flash sale) Reservation with auto-release
Reduce GSI storage + write costs on wide base items Keys-only GSI with runtime projection
Delete / clone / move a subtree (DELETE CASCADE) Cascade subtree operations
Query a hierarchical subtree (children / self+children / prefix-narrow) Querying subtrees with buildKey
Type-safe REST filters from URL query strings (?eq-status=A&btw-year=-2020-2024) Filter URL grammar
Free-form text search over a small dataset (?search=term) Text search
Bulk backfill / sweep / migration that resumes across Lambda invocations Resumable mass operations
Create / extend / verify a DynamoDB table from the same declaration the app uses Provisioning workflow

Recipes by domain

Hierarchical data + type dispatch

The SQL "one table per entity type" gets packed into a single DynamoDB table via composite keyFields + a structuralKey. These recipes cover the three canonical "list everything of type X" access patterns.

  • List records of a tier — sparse GSI keyed on the auto-populated typeField. Simplest cross-partition tier listing; one GSI covers every tier. "All states globally" / "all vehicles globally" / "all facilities globally."
  • Per-tier sparse GSI markers — one GSI per tier with a per-tier marker attribute. Finer control over projection + write cost; sharded-marker variant handles hot-partition risk at the leaf tier.
  • List records of a tier within a partition — sparse LSI sharing the base pk. Cheapest option when the listing is always scoped to a partition you already know ("all facilities under TX", never "all facilities globally").

Concurrency and time-bounded state

Optimistic concurrency + scope-freeze + resumable cleanup sweeps, composed.

  • Reservation with auto-release — user holds a resource for N minutes; expired holds auto-release on read and get swept on a schedule. Composes versionField (OC) + createdAtField + asOf (scope-freeze) + deleteListByParams (resumable sweep). Honest caveats on ABA, DynamoDB-native-TTL-as-backstop, and stealing expired holds.

Index design and storage cost

Shape the GSI for the read traffic you actually have. The toolkit composes the hand-rolled patterns as declarative options.

  • Keys-only GSI with runtime projection — declare a GSI with projection: 'keys-only' + indirect: true. Reads Query the GSI for keys, then BatchGetItem the base table with the caller's per-call fields. Cheap GSI storage + writes; rich runtime projections. Cost tables compare all / keys-only / INCLUDE tradeoffs at real item sizes.

See also Key and field design for the design framework this recipe applies.

Subtree operations

DynamoDB has no FKs, no ON DELETE CASCADE, and no native triggers. Hierarchy has to be encoded in the sort key. These recipes cover the two shapes of access pattern that result: reading a subtree, and mutating one.

  • Querying subtrees with buildKeyadapter.buildKey(values, options?) composes the three natural KeyConditionExpression shapes for a hierarchy: children-only (default), self + descendants ({self: true}), narrow-prefix ({partial: 'Dal'}). Covers the getListUnder sugar, the implicit sibling-prefix invariant for {self: true}, and the GSI-targeted-query deferral (drop to buildKeyCondition primitive for now).
  • Cascade subtree operationsdeleteAllUnder / cloneAllUnder[By] / moveAllUnder[By] against composite keyFields with relationships: {structural: true}. Leaf-first / root-first pagination, constructive-before-destructive moves, MassOpResult partial-failure surfacing, Lambda-budget resumability.

REST query surface

The URL grammars the handler + framework adapters expose to clients, with how they compile down to DynamoDB expressions.

  • Filter URL grammar?<op>-<field>=<value> (Option W) end-to-end. filterable declaration + per-field type coercion, first-character delimiter for in / btw, auto-promotion of eq-on-pk / range-on-sk clauses to KeyConditionExpression, composition with options.filter on mass ops. Cost tables for KCE-only / KCE+FE / FE-only paths.
  • Text search?search=term against lowercased searchable mirror columns. Write-side prepare hook, read-side OR across declared mirrors, case sensitivity + unicode folding tradeoffs, when to reach for OpenSearch / Algolia instead.

Bulk operations and lifecycle

Patterns for data maintenance, backfills, and table-shape management.

  • Resumable mass operationsMassOpOptions (maxItems, resumeToken, asOf, ifNotExists/ifExists) + MassOpResult (processed, skipped, failed, conflicts, cursor?). EventBridge / Step Functions / driver-script orchestration patterns; error bucketing rules (versionField conflicts vs plain CCFs); maxItems soft-cap semantics.
  • Provisioning workflowplanTable (read-only) / ensureTable (ADD-only apply) / verifyTable (drift detection). Descriptor-row round-trip for metadata DescribeTable can't see (filterable, searchable, versionField, …). Placement in IaC-owned vs toolkit-owned vs CLI-only deploy pipelines.

Adding a recipe

Recipes follow a consistent shape so readers know where to look:

  1. Title + SQL-equivalent question in a blockquote at the top. Tell the reader what problem the recipe solves, framed in SQL terms they already know.
  2. "Why DynamoDB makes you compose" (or "Why X gets expensive" / "Why you can't just Y") — one or two paragraphs that name the specific DynamoDB primitives missing vs SQL, and why the naive approach fails.
  3. The pattern — runnable declaration (Adapter + indices + hooks). Minimal but complete.
  4. Worked code for each operation the pattern supports.
  5. Cost + capacity — storage, RCU / WCU, partition-throughput ceilings. Tables where honest math informs the decision.
  6. Alternatives or middle grounds — "projection: 'all' vs keys-only," "DynamoDB native TTL vs custom sweep," etc. Recipes are opinionated; alternatives show the opinion is informed, not absolute.
  7. When this fits / when it doesn't — bullet lists. If every point under "doesn't fit" applies to your use case, stop reading and pick a different pattern.
  8. Related — cross-references to Concepts / Adapter reference / other recipes.

Language rules of thumb:

  • Names the SQL equivalent upfront, then drops the framing once the reader is oriented.
  • Tables beat prose for cost / capacity / comparison sections.
  • Every dollar figure or throughput number has a unit and a region (e.g., "$0.25/GB/mo in us-east-1"). If the number changes with partition size, say so.
  • "When it doesn't fit" earns credibility. Avoid selling the pattern past its range.

Related

  • Concepts — the vocabulary every recipe assumes (pk, sk, GSI, LSI, projection, structural key, technicalPrefix).
  • Key and field design — how to choose partition / sort keys, when to reach for LSI vs GSI, which technical fields actually earn their bytes. Read before you shape a new table; recipes apply these choices to specific problems.
  • Adapter: Constructor options — full declaration reference for keyFields, structuralKey, indices, versionField, createdAtField, typeField, relationships, etc.

Clone this wiki locally