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 .dnml file directly in the folder is one request. The request name must 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}"
The same request in the app, as a DNML file, and in the CLI.
  • [variables] and [defaults] hold request values and the AWS profile.
  • Each [[dynamodb.…]] block is one operation.
  • dependsOn refers 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 that limit needs.

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 version is not valid.
  • Dynomate accepts all 1.x versions. Keys that it does not know are unknown keys.
  • A higher major version, such as "2.0", causes DNML_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