{
  "human": {
    "name": "John Doe",
    "age": 30,
    "address": {
      "street": "123 Main St",
      "city": "San Francisco",
      "state": "CA",
      "zip": "94105"
    }
  },
  "pet": {
    "name": "Fido",
    "age": 5,
    "species": "dog"
  }
}

DryValues

Basic Hydration

DryValues are the simplest form of templating. They allow you to reference a value from the hydrator object and use it in your DryMerge script. For example, if we wanted to use the name field from the human object, we could do so like this:

name:
  dry_value": "{{human.name}}"

When we evaluate this in the DryMerge engine, it will turn into…

{
  "name": "John Doe"
}

Super simple!

Schema Validation

A lot of the time you need to impose guarantees on the structure of your DryValue to ensure that you’re getting passed the right data. You can do this with the schema field. For example, if we wanted to ensure that the name field was a string, we could do so like this:

name:
  dry_value: '{{human.name}}'
  schema: 
    type: string

The schema field defines a JSON schema that the value must match. If it doesn’t match, the DryMerge engine will throw an error. You can read more about JSON schemas here.

Default Values

If there’s an error with validation but it’s recoverable, you might want to add a default value. You can do this with the default field. For example, if we wanted to default to “Unknown” if the name field was missing, we could do so like this:

custom-name:
    dry_value: '{{human.name}}'
    schema:
      type: string
    default: "Unknown"

Say the human API changed and name is now, for some reason, an integer. In this case, we’d fail validation but instead of erroring out, we’d default to "Unknown".

DryStrings

Basic Hydration

Strings are really common and thus really special. For DryValues, you can only index into one object. For DryStrings, you can index into as many objects as you want. For example, if we wanted to use the name field from the human object and the name field from the pet object, we could do so like this:

  custom-name: 
    dry_string: "{{human.name}} and {{pet.name}}"

When we evaluate this in the DryMerge engine, it will turn into…

{
  "custom-name": "John Doe and Fido"
}

Regex Validation

Just like with DryValues, a lot of the time you need to impose guarantees on the structure of your DryString to ensure that you’re getting passed the right data. You can do this with the matches field. For example, if we wanted to ensure that the name field didn’t contain any numbers or special characters, we could do…

custom-name: 
  dry_string: "{{human.name}} and {{pet.name}}"
  matches: "^[a-zA-Z ]+$"

Default Values

Again, just like with DryValues, if there’s an error with validation but it’s recoverable, you might want to add a default value. You can do this with the default field. For example, if we wanted to default to “Unknown” if the name field was missing, we could do so like this:

custom-name:
    dry_string: "{{human.name}} and {{pet.name}}",
    matches: "^[a-zA-Z ]+$",
    default:
      pet.name: "Unknown"
      human.name: "Unknown"

So if for some reason our human API changed and name was now an integer, we’d fail validation but instead of causing an unrecoverable error, we’d default human.name to "Unknown".

Slices

Sometimes you want to take a substring of a string to control length (especially with LLM context windows). You can specify substring offsets with the slice field.

custom-name:
  dry_string: "{{human.name}} and {{pet.name}}",
  matches: "^[a-zA-Z ]+$",
  default:
    pet.name: "Unknown"
    human.name: "Unknown"
  slice:
    start: 5
    end: 10

This would take substring of characters 5-10 in the hydrated string. Note that the slice will fail gracefully: if the end field is too big, it’ll take the minimum of end and length. Similarly, if slice goes beyond the initial length, it’ll default to a blank string.

Types of Context

DryMerge has a few different types of base objects that you can reference during templating. They’re summarized below:

Context TypeDescriptionAssociated Process
contextObjects obtained from dependency results or from users (or triggers/crons using the with syntax) passing arguments.Calling DryMerge workflows; dependency execution.
secretsObjects obtained from the global organization secret store.Secrets getting added through the CLI or via the store syntax
mapIndividual elements of an iterable being gone through via the map syntax.Created when a node uses the map syntax over the elements in that iterable’s map
metaContext relevant to infrastructure operation. As of now, pops up when trigger nodes kick off their workflows. Specifically has compare (which itself contains before, after, diff, and reverse_diff from the trigger diffing operations).When workflows are kicked off by triggers.
templateArguments passed when DryMerge entities are instantiated from templates. The only type of context that doesn’t need to be accessed through a dry_string or dry_value. Never contains runtime data.Instatiation of DryMerge entities via a template (through an import usually, sometimes through an API).
resultIndexing into the result of a DryMerge call. Only currently referencable in store operations.After a node has finished executing