Skip to main content
A resolvable is how you read a value off a resource before that value exists. You write resource.res.property, and formae fills in the real value at apply time, once the resource that produces it has actually been created. Resolvables exist for two things:
  1. Referencing properties of other resources (a subnet needs a VPC ID, a database needs subnet IDs).
  2. Referencing secrets without ever exposing their value.
The examples below use local to bind a resource to a variable so it can be referenced later via .res. A local still has to be mentioned inside the forma block to actually be created. See Write your first forma for the full pattern.

Why resolvables exist

Infrastructure resources depend on each other, and the values they depend on often don’t exist until the dependency is created (an AWS-generated ID, an ARN, a randomly generated password). Resolvables let you write that dependency declaratively, without manually ordering resources or copying values by hand. formae:
  • Detects when a referenced property is available.
  • Waits for it if the resource that produces it is still being created.
  • Injects the resolved value at the right point in the apply.
This is what lets you write vpc.res.vpcId in a subnet’s definition even though the VPC doesn’t exist yet: formae works out that the subnet depends on the VPC, creates the VPC first, and substitutes the real ID.

Referencing resource properties

Referencing secrets

A secret is created like any other resource, then referenced through .res the same way. Wrapping the value with formae.value(...).opaque keeps it out of logs, output, and CLI results while still letting other resources consume it.
  • formae.value() wraps the password.
  • .opaque marks it as a secret that is never displayed.
  • .setOnce generates it once and reuses that value on every subsequent apply.
  • dbSecret.res.secretString references the secret’s value in the database configuration.
See Values for more on .opaque and .setOnce.

Embedding resolvables in strings

.res gives you a resolvable as a field’s entire value. Sometimes you need to splice a resolved value into the middle of a larger string, for example, a value that only exists after another resource is created, but has to appear inside a code block or config template. formae.embed(...) does this: write the surrounding text as a string and interpolate any resolvable with \(...). formae resolves each reference at apply time and substitutes the real value into the text. Both resources still apply in a single pass, the referenced resource is ordered first automatically. A CloudFront Function whose JavaScript needs the generated ID of a Key Value Store is the canonical case. Without embedding you would apply the store, copy its ID by hand, and re-apply the function. With it, one apply does both:
At apply time, \(store.res.id) becomes the real Key Value Store ID (for example a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d), and the function is created with that value already in its source. A few things to know:
  • A field has to opt in to accept embeds. The plugin’s schema decides whether a field can be embedded. If a field rejects formae.embed(...), reference the whole value with .res instead.
  • formae extract round-trips embeds. Extracting a managed resource regenerates the formae.embed("…\(…)…") call rather than the resolved value, so re-applying is a no-op and the reference is never flattened to a literal.
  • You can embed more than one reference in the same string, and mix them with formae.value(...) secrets exactly as in any other field.

Resolvables in targets

Resolvables aren’t limited to resource properties, they can also appear in target configurations. This enables cross-plugin patterns where one plugin provides infrastructure and another plugin’s target resolves its connection details from it:
The at() method indexes into the endpoints Mapping by key: formae resolves it to the actual URL (for example http://localhost:3000) at apply time. See Target resolvables for the full pattern.

Collection resolvables

When a resource property resolves to a collection (Mapping or Listing), use at() to reference individual items:
See the Collection Resolvables section of the plugin SDK docs for details on defining these in your schemas.

See also

  • Values: the .opaque and .setOnce modifiers used with secrets.
  • Target: target resolvables and cross-plugin connection resolution.
  • Properties: the CLI-facing counterpart to resolvables.