DNML
DNML (Dynomate Markup Language) is the file format of a Dynomate request. A request is one .dnml file with named, ordered operations for DynamoDB and Athena.
One File, Two Runners
The app and the CLI read, validate and run the same .dnml files with the same rules. You can save a file in the app and run it in the CLI, or edit it by hand and open it in the app.
Collection Layout
A collection is a folder with a collection.dnml marker file and zero or more request files:
orders/
collection.dnml # collection marker (required)
find-and-update-order.dnml # request named "find-and-update-order"
list-open-orders.dnml # request named "list-open-orders"
archive/
old-request.dnml # not discovered: nested one level too deep - A folder is a collection only if it contains a valid
collection.dnml. - Each other
.dnmlfile directly in the folder is one request. The requestnamemust be the same as the filename. - Dynomate does not find request files in subfolders. It does not read other file types.
- If a file is not valid, Dynomate shows a diagnostic with its path. The other requests stay available.
The CLI can also run a .dnml file outside a collection. See also Request collections.
Running Example
The DNML, Requests and CLI pages use one example request. find-and-update-order finds the most recent order for an account, marks it reviewed, and reads it back:
orders/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}" $ dynomate-cli requests run ./orders/find-and-update-order.dnml
find-and-update-order: success
Find recent order: success
result: {"count":1,"items":[{"accountId":"account#42","orderId":"order#9812","status":"PENDING","total":42}],"lastEvaluatedKey":"eyJvcmRlcklkIjp7IlMiOiJvcmRlciM5ODEyIn0sImFjY291bnRJZCI6eyJTIjoiYWNjb3VudCM0MiJ9fQ==","pages":1,"requestId":"1f999a0f-0ec9-4dba-bd58-c012239a26db","scannedCount":1}
Mark reviewed: success
result: {"attributes":{"accountId":"account#42","orderId":"order#9812","status":"REVIEWED","total":42},"requestId":"38ea89c4-6e57-48d5-a038-66ab17034323"}
Read back: success
result: {"found":true,"item":{"accountId":"account#42","orderId":"order#9812","status":"REVIEWED","total":42},"requestId":"d2946fe3-e6b1-4ede-a4ce-aa4cd1365319"} [variables]and[defaults]hold request values and the AWS profile.- Each
[[dynamodb.…]]block is one operation. dependsOnrefers to another operation by its exact name.${find_recent_order.items[0].orderId}reads the query result through the normalized name.${cast(pageSize, 'integer')}changes the string"1"into the integer thatlimitneeds.
If the query returns no items, items[0] does not resolve, and the update fails before its AWS call.
TOML Basis
A DNML file is a valid TOML 1.1 document in UTF-8, without a byte order mark. In TOML 1.1, an inline table can use many lines and end with a trailing comma.
DNML adds a set of keys for each operation, ${…} templates and run rules. In DNML, the order of the operation blocks is important. In TOML, it is not.
A minimal request:
version = "1.0"
name = "list-users"
[[dynamodb.query]]
name = "Users by tenant"
tableArn = "arn:aws:dynamodb:us-east-1:123456789012:table/Users"
keyConditionExpression = "#pk = :pk"
[dynamodb.query.expressionAttributeNames]
"#pk" = "pk"
[dynamodb.query.expressionAttributeValues]
":pk" = "tenant#acme" Versioning
Each request file starts with version = "1.0", in the form MAJOR.MINOR.
- A request file without
versionis not valid. - Dynomate accepts all
1.xversions. Keys that it does not know are unknown keys. - A higher major version, such as
"2.0", causesDNML_UNSUPPORTED_VERSION. - When Dynomate saves a request, it writes
version = "1.0"on the first line.
The collection marker also has version = "1.0".
Where to Next
- Document structure: keys, blocks and names.
- Variables and expressions: templates and casts.
- Dependencies and execution: run order and errors.
- Safety: destructive operations and endpoints.
- Operation reference: all operations and keys.
- Examples: complete request files.
- Schema and editor setup: validation in your editor.
- Build a request and CLI: the app and the terminal.