Browse documentation

Examples

Save these complete requests in a collection, then run them in the app or with the CLI.

The file name must match name. See Naming rules. Replace the profile names, account IDs, table ARNs and cluster ARNs with your values.

find-and-update-order.dnml

The running example: find, update and read back an order.

Operations: Query, Update item, Get item.

DNML
find-and-update-order.dnml
version = "1.0"name = "find-and-update-order"description = "Find the most recent order for an account, mark it reviewed, and read it back."[variables]accountId = "account#42"pageSize = "1"[defaults]profileName = "commerce-dev"[[dynamodb.query]]name = "Find recent order"tableArn = "arn:aws:dynamodb:ap-southeast-2:111122223333:table/Orders"keyConditionExpression = "accountId = :accountId"limit = "${cast(pageSize, 'integer')}"scanIndexForward = false[dynamodb.query.expressionAttributeValues]":accountId" = "${accountId}"[[dynamodb.update]]name = "Mark reviewed"dependsOn = "Find recent order"tableArn = "arn:aws:dynamodb:ap-southeast-2:111122223333:table/Orders"updateExpression = "SET #status = :status"[dynamodb.update.keyValues]accountId = "${accountId}"orderId = "${find_recent_order.items[0].orderId}"[dynamodb.update.expressionAttributeNames]"#status" = "status"[dynamodb.update.expressionAttributeValues]":status" = "REVIEWED"[[dynamodb.get]]name = "Read back"dependsOn = "Mark reviewed"tableArn = "arn:aws:dynamodb:ap-southeast-2:111122223333:table/Orders"consistentRead = true[dynamodb.get.keyValues]accountId = "${accountId}"orderId = "${find_recent_order.items[0].orderId}"
Lines 1–20 of 46

chained-query-update.dnml

The same chain with a condition and a date-time value.

Operations: Query, Update item, Get item.

DNML
chained-query-update.dnml
version = "1.0"name = "chained-query-update"description = "Locate the most recent order for an account and mark it reviewed."[variables]accountId = "account#42"targetStatus = "REVIEWED"pageSize = 10[defaults]profileName = "${AWS_PROFILE}"[[dynamodb.query]]name = "Find recent order"tableArn = "arn:aws:dynamodb:ap-southeast-2:111122223333:table/Orders"keyConditionExpression = "#accountId = :accountId"limit = "${pageSize}"scanIndexForward = false[dynamodb.query.expressionAttributeNames]"#accountId" = "accountId"[dynamodb.query.expressionAttributeValues]":accountId" = "${accountId}"[[dynamodb.update]]name = "Mark order reviewed"dependsOn = "Find recent order"tableArn = "arn:aws:dynamodb:ap-southeast-2:111122223333:table/Orders"updateExpression = "SET #status = :status, reviewedAt = :now"conditionExpression = "attribute_exists(orderId)"returnValues = "all-new"[dynamodb.update.keyValues]accountId = "${accountId}"orderId = "${find_recent_order.items[0].orderId}"[dynamodb.update.expressionAttributeNames]"#status" = "status"[dynamodb.update.expressionAttributeValues]":status" = "${targetStatus}"":now" = 2026-09-05T03:12:41Z[[dynamodb.get]]name = "Read back"dependsOn = ["Mark order reviewed"]tableArn = "arn:aws:dynamodb:ap-southeast-2:111122223333:table/Orders"consistentRead = true[dynamodb.get.keyValues]accountId = "${accountId}"orderId = "${find_recent_order.items[0].orderId}"
Lines 1–20 of 53

casts-and-escaping.dnml

All four casts, typed values and literal dollar signs.

Operations: Query, Put item.

DNML
casts-and-escaping.dnml
version = "1.0"name = "casts-and-escaping"description = "Convert string inputs explicitly and compose literal dollars with interpolation."[variables]accountId = "account#42"PAGE_SIZE = "100"SCAN_FORWARD = "true"numericText = "12.50"count = 5price = 42templateLookingText = '${price}'[defaults]profileName = "commerce-dev"region = "ap-southeast-2"[[dynamodb.query]]name = "Read orders"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/Orders"keyConditionExpression = "accountId = :accountId"limit = "${cast(PAGE_SIZE, 'integer')}"scanIndexForward = "${cast(SCAN_FORWARD, 'boolean')}"consistentRead = trueexpressionAttributeValues = { ":accountId" = "${accountId}" }# The query receives integer 100 and boolean true. Without casts, these input# strings would fail the fields' type checks; no automatic coercion occurs.[[dynamodb.put]]name = "Write resolved values"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/ExampleValues"[dynamodb.put.item]id = "casts-and-escaping"asInteger = "${cast(PAGE_SIZE, 'integer')}"asNumber = "${cast(numericText, 'number')}"asBoolean = "${cast(SCAN_FORWARD, 'boolean')}"asString = "${cast(count, 'string')}"stringAttribute = { S = "${cast(count, 'string')}" }numberAttribute = { N = "${cast(count, 'string')}" }exactDecimal = { N = "${numericText}" }# A whole-value expression preserves its type. Both typed payload casts above# return the string "5"; exactDecimal supplies the decimal string "12.50"# directly to strict N, without an intermediate binary64 conversion.cost = "Cost: $$${price}"       # string "Cost: $42"escapedTemplate = "$${price}" # literal string "${price}"fourDollars = "$$$$"          # string "$$"literalAmount = "$42"         # string "$42"wholeValue = "${price}"       # number 42substitutedText = "${templateLookingText}" # literal string "${price}"# Scan after TOML decoding: $$ emits $, ${...} resolves, other $ stays literal.# Neither emitted nor substituted text is scanned again.## Invalid examples (comments only, so this remains a valid document):#   ${cast(PAGE_SIZE, 'String')} is DNML_SYNTAX: target names are lowercase.#   Casting the string "1.5" to 'integer' is DNML_TYPE_MISMATCH.#   Casting the string "NaN" to 'number' is DNML_TYPE_MISMATCH.#   Casting the string "yes" to 'boolean' is DNML_TYPE_MISMATCH.#   Casting a map, array, set, binary, or null to 'string' is DNML_TYPE_MISMATCH.#   No cast trims whitespace; an integer string " 100 " is rejected.#   { S = "${count}" } and { N = "${count}" } are DNML_TYPED_VALUE:#   count resolves to a number, and these typed payloads require strings.
Lines 1–20 of 66

lossless-reuse.dnml

Copy items between tables with no loss of type or precision.

Operations: Put item, Get item.

DNML
lossless-reuse.dnml
version = "1.0"name = "lossless-reuse"description = "Reuse fetched items and selected attributes without changing DynamoDB types or values."# SourceValues and ReplicaValues both have a string partition key named id.# This example covers all ten DynamoDB attribute types. The 38-digit decimal# cannot be copied faithfully through a binary64 number.[defaults]profileName = "commerce-dev"region = "ap-southeast-2"[[dynamodb.put]]name = "Seed source"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/SourceValues"[dynamodb.put.item]id = "all-types"stringValue = { S = "text" }preciseNumber = { N = "12345678901234567890.123456789012345678" }binaryValue = { B = "AAEC/w==" }booleanValue = { BOOL = true }nullValue = { NULL = true }listValue = { L = [ { N = "1.25" }, { B = "AA==" }, { NULL = true } ] }mapValue = { M = { child = { NS = ["1", "2.5"] }, "customer.name" = "a literal dotted key" } }stringSet = { SS = ["blue", "green"] }numberSet = { NS = ["1", "2.5", "12345678901234567890123456789012345678"] }binarySet = { BS = ["AA==", "/w=="] }singletonTagMap = { M = { S = "a map entry named S" } }[[dynamodb.get]]name = "Load"dependsOn = "Seed source"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/SourceValues"consistentRead = truekeyValues = { id = "all-types" }[[dynamodb.put]]name = "Copy item"dependsOn = "Load"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/ReplicaValues"item = "${load.item}"# Copy item preserves every attribute recursively: numbers remain exact, binary# remains binary, sets remain sets, and singletonTagMap remains a map, not S.# A displayed JSON representation does not determine the copied input types.[[dynamodb.put]]name = "Copy selected attributes"dependsOn = "Load"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/ReplicaValues"[dynamodb.put.item]id = "selected-attributes"stringValue = "${load.item.stringValue}"preciseNumber = "${load.item.preciseNumber}"binaryValue = "${load.item.binaryValue}"booleanValue = "${load.item.booleanValue}"nullValue = "${load.item.nullValue}"listValue = "${load.item.listValue}"mapValue = "${load.item.mapValue}"stringSet = "${load.item.stringSet}"numberSet = "${load.item.numberSet}"binarySet = "${load.item.binarySet}"singletonTagMap = "${load.item.singletonTagMap}"nestedNumberSet = "${load.item.mapValue.child}"nestedBinary = "${load.item.listValue[1]}"# Selected subtrees have the same lossless guarantee as the whole item.# mapValue's literal customer.name key cannot be selected directly in v1, but# it is valid data and is preserved when mapValue or the whole item is reused.# Text interpolation and explicit casts deliberately convert values; this# example uses only whole-value references for the copied data.
Lines 1–20 of 73

transfer-order-ownership.dnml

Move an order to another account in one transaction with a version check.

Operations: Get item, Write transaction, PartiQL statement.

DNML
transfer-order-ownership.dnml
version = "1.0"name = "transfer-order-ownership"description = "Atomically move an order with the documented item shape."# Preconditions for this example:# Orders has string keys accountId (partition) and orderId (sort).# The source item exists and has exactly these five required fields:# accountId, orderId, and status are strings; version is a positive integer;# payload is a map containing arbitrary application data.# fromAccount and toAccount differ. Every concurrent update to these items# increments version, so a changed source fails the transaction's condition.# Additional top-level fields are not copied; place application data in payload.[variables]fromAccount = "account#42"toAccount = "account#77"orderId = "order#1049"[defaults]profileName = "commerce-dev"region = "ap-southeast-2"[execution]allowDestructive = true[[dynamodb.get]]name = "Load order"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/Orders"consistentRead = truekeyValues = { accountId = "${fromAccount}", orderId = "${orderId}" }[[dynamodb.transactWrite]]name = "Move order"dependsOn = "Load order"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/Orders"actions = [  {    action = "put",    conditionExpression = "attribute_not_exists(accountId)",    item = {      accountId = "${toAccount}",      orderId = "${load_order.item.orderId}",      status = "${load_order.item.status}",      version = "${load_order.item.version}",      payload = "${load_order.item.payload}",    },  },  {    action = "delete",    keyValues = { accountId = "${fromAccount}", orderId = "${orderId}" },    conditionExpression = "attribute_exists(accountId) AND #status <> :locked AND #version = :version",    expressionAttributeNames = { "#status" = "status", "#version" = "version" },    expressionAttributeValues = {      ":locked" = "LOCKED",      ":version" = "${load_order.item.version}",    },  },]# The destination condition belongs to the put itself. The put and delete use# different keys, so each item has only one action in this transaction.[[dynamodb.executeStatement]]name = "Verify"dependsOn = "Move order"statement = 'SELECT "accountId", "orderId", "status" FROM "Orders" WHERE "accountId" = ? AND "orderId" = ?'parameters = ["${toAccount}", "${orderId}"]consistentRead = true
Lines 1–20 of 68

table-lifecycle.dnml

Create, fill, scan, truncate and delete a table on DynamoDB Local. It needs confirmation or approval.

Operations: Create table, Import items, Scan, Truncate table, Delete table.

DNML
table-lifecycle.dnml
version = "1.0"name = "table-lifecycle"description = "Recreate the dev Orders table from seed data, verify, then tear down."[execution]onError = "stop"allowDestructive = true[defaults]profileName = "local"region = "ap-southeast-2"endpointUrl = "http://localhost:8000"[[dynamodb.createTable]]name = "Create table"tableArn = "arn:aws:dynamodb:ap-southeast-2:000000000000:table/Orders-dev"billingMode = "pay-per-request"ifNotExists = truepartitionKey = { name = "accountId", type = "S" }sortKey = { name = "orderId", type = "S" }timeToLive = { attribute = "expiresAt" }globalSecondaryIndexes = [  {    name = "status-index",    partitionKey = { name = "status", type = "S" },    sortKey = { name = "orderId", type = "S" },    projection = "keys-only",  },][[dynamodb.import]]name = "Seed items"dependsOn = "Create table"tableArn = "arn:aws:dynamodb:ap-southeast-2:000000000000:table/Orders-dev"conflictMode = "overwrite"[dynamodb.import.source]type = "inline"items = [  {    accountId = "account#1",    orderId = "order#1001",    status = "NEW",    total = { N = "19.99" },    tags = { SS = ["gift", "priority"] },  },  { accountId = "account#1", orderId = "order#1002", status = "SHIPPED", total = 5 },][[dynamodb.scan]]name = "Count seeded"dependsOn = "Seed items"tableArn = "arn:aws:dynamodb:ap-southeast-2:000000000000:table/Orders-dev"indexName = "status-index"maxPages = 0[[dynamodb.truncate]]name = "Clear table"dependsOn = "Count seeded"tableArn = "arn:aws:dynamodb:ap-southeast-2:000000000000:table/Orders-dev"segments = 4guard = { expectTableName = "Orders-dev", expectItemCountAtMost = 1000 }[[dynamodb.deleteTable]]name = "Drop table"dependsOn = "Clear table"tableArn = "arn:aws:dynamodb:ap-southeast-2:000000000000:table/Orders-dev"ifExists = trueguard = { expectTableName = "Orders-dev" }
Lines 1–20 of 69

ttl-control.dnml

Shows that ttlEnabled and enabled are different keys.

Operations: Update time to live.

DNML
ttl-control.dnml
version = "1.0"name = "ttl-control"description = "Distinguish the TTL setting from whether an operation runs."[defaults]profileName = "commerce-dev"region = "ap-southeast-2"[[dynamodb.updateTimeToLive]]name = "Disable TTL"tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/Orders"attribute = "expiresAt"ttlEnabled = false# enabled is omitted, so it defaults to true: execute and disable TTL.# The required TTL field is distinct from the common operation enabled control.[[dynamodb.updateTimeToLive]]name = "Skipped TTL activation"enabled = falsetableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/Orders"attribute = "expiresAt"ttlEnabled = true# This operation is skipped because the common enabled field is false.# With enabled omitted or true, ttlEnabled = true would enable TTL and the# operation would be classified as destructive.
Lines 1–20 of 27

import-export.dnml

Export orders to a snapshot, then import from three sources.

Operations: Export items, Import items.

DNML
import-export.dnml
version = "1.0"name = "import-export"description = "Export production orders to a local snapshot, then load a curated subset into staging."[variables]prodAccount = "111122223333"stagingAccount = "444455556666"[execution]allowDestructive = true[[dynamodb.export]]name = "Export production"profileName = "commerce-prod"tableArn = "arn:aws:dynamodb:ap-southeast-2:${prodAccount}:table/Orders"snapshot = truefilterExpression = "#status <> :archived"target = { type = "file", path = "exports/", format = "dynamodb-jsonl" }[dynamodb.export.expressionAttributeNames]"#status" = "status"[dynamodb.export.expressionAttributeValues]":archived" = "ARCHIVED"[[dynamodb.import]]name = "Load staging from export"dependsOn = "Export production"profileName = "commerce-staging"tableArn = "arn:aws:dynamodb:ap-southeast-2:${stagingAccount}:table/Orders"conflictMode = "skip-existing"concurrency = 4source = { type = "file", path = "${export_production.outputPath}", format = "jsonl" }[[dynamodb.import]]name = "Load fixtures from S3"profileName = "commerce-staging"tableArn = "arn:aws:dynamodb:ap-southeast-2:${stagingAccount}:table/Orders"conflictMode = "overwrite"source = { type = "s3", bucket = "commerce-fixtures", key = "orders/staging-fixtures.csv", region = "ap-southeast-2", format = "csv" }[[dynamodb.import]]name = "Mirror reference table"profileName = "commerce-staging"tableArn = "arn:aws:dynamodb:ap-southeast-2:${stagingAccount}:table/Products"[dynamodb.import.source]type = "dynamodb-table"profileName = "commerce-prod"tableArn = "arn:aws:dynamodb:ap-southeast-2:${prodAccount}:table/Products"segments = 8
Lines 1–20 of 51

athena-report.dnml

An Athena query with text, number and date parameters.

Operations: Athena query.

DNML
athena-report.dnml
version = "1.0"name = "athena-report"description = "Find paid orders using positional Athena execution parameters."[defaults]profileName = "analytics"region = "ap-southeast-2"workgroup = "dynomate"[[athena.query]]name = "Paid orders"database = "commerce"maxRows = 500timeoutMs = 300000sql = """SELECT order_id, total, order_dateFROM ordersWHERE status = ?  AND total >= ?  AND order_date >= ?"""executionParameters = ["'PAID'", "100", "CAST('2026-09-01' AS DATE)"]# Parameters bind to the three unquoted ? placeholders from left to right.# Each TOML string contains a SQL literal or expression; DNML does not add SQL# quotes. 'PAID' includes the required text quotes, 100 is a numeric SQL literal,# and CAST(...) supplies a date. PREPARE and EXECUTE are not required.# This SQL CAST is distinct from DNML's ${cast(...)} expression.
Lines 1–20 of 28

dsql-ledger.dnml

Record a payment in an Amazon Aurora DSQL ledger in one transaction that retries on conflict, then read the balance back.

Operations: Aurora DSQL query, DSQL transaction, List DSQL tables, Describe DSQL table.

DNML
dsql-ledger.dnml
version = "1.1"name = "dsql-ledger"description = "Record a payment in an Aurora DSQL ledger and read the balance back."# A dsql operation makes this a DNML 1.1 document. The cluster ARN supplies the# region; there is no endpointUrl and no database (always postgres). Every# operation below inherits the cluster and role, and shares one connection per# profile, cluster and role for the whole run.[variables]ACCOUNT_ID = "4b0c5a52-1f2e-4b43-9a55-0e6f6d9a0c21"[defaults]profileName = "ledger"clusterArn = "arn:aws:dsql:us-east-1:123456789012:cluster/abcdefghijklmnopqrstuvwxyz"databaseRole = "ledger_writer"[[dsql.query]]name = "Account"sql = '''SELECT id, balance, updated_atFROM ledger.accountsWHERE id = $1'''parameters = ["${ACCOUNT_ID}"]# The string parameter is sent with an unknown type, so the server compares it# with the uuid column without a cast.[[dsql.transaction]]name = "Record payment"dependsOn = "Account"statements = [  {    sql = "UPDATE ledger.accounts SET balance = balance - $2, updated_at = now() WHERE id = $1",    parameters = ["${ACCOUNT_ID}", { N = "12.50" }],  },  {    sql = "INSERT INTO ledger.payments (account_id, amount) VALUES ($1, $2)",    parameters = ["${ACCOUNT_ID}", { N = "12.50" }],  },]retryOnConflict = true# { N = "12.50" } binds an exact numeric. Retrying on a conflict is safe here:# the UPDATE reads the balance it writes, and a conflict rolls back the whole# transaction before the retry. Retrying a write whose values were read by an# earlier operation could overwrite a concurrent change instead.[[dsql.query]]name = "Readback"dependsOn = ["Account", "Record payment"]sql = "SELECT balance FROM ledger.accounts WHERE id = $1"parameters = ["${account.statements[0].rows[0].id}"]maxRows = 1[[dsql.listTables]]name = "Ledger tables"schema = "ledger"expression = "pay%"[[dsql.describeTable]]name = "Payments table"schema = "ledger"tableName = "payments"[[dsql.query]]name = "Install cents helper"enabled = falsedatabaseRole = "admin"sql = '''CREATE FUNCTION ledger.cents(amount numeric) RETURNS bigintLANGUAGE sql IMMUTABLEAS $fn$ SELECT (amount * 100)::bigint $fn$'''# Run once as admin: the prepared request flags the admin connection. The tagged# dollar quote $fn$ passes through DNML unchanged; a bare $$ would be read as# DNML's escape for one literal $.
Lines 1–20 of 78

settle-order.dnml

Settle an order with values from an earlier query, with no retry on conflict. The order ID comes from an input.

Operations: Aurora DSQL query, DSQL transaction.

DNML
settle-order.dnml
version = "1.1"name = "settle-order"description = "Settle one pending order and record the settlement."[defaults]profileName = "commerce-dev"clusterArn = "arn:aws:dsql:us-east-1:111122223333:cluster/exampleclusterid0123456789"databaseRole = "app_writer"[execution]onError = "stop"[[dsql.query]]name = "Load order"sql = '''SELECT id, status, total, currencyFROM ordersWHERE id = $1 AND status = 'PENDING''''parameters = ["${ORDER_ID}"][[dsql.transaction]]name = "Settle"dependsOn = "Load order"statements = [  {    sql = "UPDATE orders SET status = 'SETTLED', settled_at = now() WHERE id = $1 AND status = 'PENDING'",    parameters = ["${load_order.statements[0].rows[0].id}"],  },  {    sql = "INSERT INTO order_events (order_id, event, amount, currency) VALUES ($1, 'settled', $2, $3)",    parameters = ["${load_order.statements[0].rows[0].id}", "${load_order.statements[0].rows[0].total}", "${load_order.statements[0].rows[0].currency}"],  },][[dsql.query]]name = "Read back"dependsOn = "Settle"sql = "SELECT id, status, settled_at FROM orders WHERE id = $1"parameters = ["${ORDER_ID}"]maxRows = 1
Lines 1–20 of 41

build-status-index.dnml

Build an index as admin, wait for the job, then describe the table.

Operations: Aurora DSQL query, Wait for job, Describe DSQL table.

DNML
build-status-index.dnml
version = "1.1"name = "build-status-index"description = "Create an index asynchronously, wait for its job, then describe the table."[defaults]profileName = "commerce-admin"clusterArn = "arn:aws:dsql:us-east-1:111122223333:cluster/exampleclusterid0123456789"databaseRole = "admin"[[dsql.query]]name = "Create index"sql = "CREATE INDEX ASYNC orders_by_status ON orders (status, created_at)"[[dsql.waitForJob]]name = "Wait for index"dependsOn = "Create index"timeoutMs = 900000jobId = "${create_index.statements[0].rows[0].job_id}"pollIntervalMs = 2000[[dsql.describeTable]]name = "Orders table"dependsOn = "Wait for index"schema = "public"tableName = "orders"

inspect-orders.dnml

List schemas and tables, describe a table and read recent orders. The cluster ID in the ARN comes from an input.

Operations: List schemas, List DSQL tables, Describe DSQL table, Aurora DSQL query.

DNML
inspect-orders.dnml
version = "1.1"name = "inspect-orders"description = "List schemas and order tables, describe the orders table, and read recent orders."[defaults]profileName = "commerce-dev"clusterArn = "arn:aws:dsql:us-east-1:111122223333:cluster/${CLUSTER_ID}"databaseRole = "app_reader"[[dsql.listSchemas]]name = "Schemas"[[dsql.listTables]]name = "Order tables"schema = "public"expression = "order%"[[dsql.describeTable]]name = "Orders table"tableName = "orders"[[dsql.query]]name = "Recent orders"sql = '''SELECT id, status, total, created_atFROM ordersWHERE created_at >= now() - $1::intervalORDER BY created_at DESCLIMIT $2'''parameters = ["7 days", 100]maxRows = 25
Lines 1–20 of 32

collection.dnml

The marker file that makes a folder a collection.

See Collection marker.

DNML
collection.dnml
# Collection marker (collection.dnml). One of these per collection directory.version = "1.0"type = "dynomate-collection"created = 2026-09-05T00:00:00Z[metadata]description = "Order service runbooks"author = "Platform team"[metadata.tags]service = "orders"