Skip to main content
Resolvables enable resources to reference properties of other resources that aren’t known until apply time. For example, a Subnet can reference a VPC’s ID before the VPC exists, and formae resolves the actual value when the VPC is created.

When to Use Resolvables

Use resolvables when a resource property depends on output from another resource:
  • Subnet needs VPC ID (generated when VPC is created)
  • Security Group references VPC ID
  • IAM Policy references Role ARN
  • Any property that uses an identifier generated by cloud infrastructure

Defining a Resolvable Class

Each resource type that can be referenced needs a Resolvable class:
The hidden res property lets other resources reference this resource’s properties.

Accepting Resolvable References

Use a union type to accept either a literal value or a resolvable reference:

Embedding resolvables in string fields

String|formae.Resolvable lets a field be either a literal or a reference, where the whole value is one or the other. To let users splice a reference into the middle of a string (the user-facing side is covered in Embedding resolvables in strings), widen the field to also accept formae.Embedded:
A user can then write functionCode = formae.embed("const id = '\(store.res.id)'; …"). formae pulls each embedded reference into the same dependency graph as a whole-value resolvable, resolves it at apply time, and hands your plugin the fully assembled plain string. Your plugin never sees an embed marker, exactly as with formae.Resolvable. Extraction is handled for you too: formae regenerates the formae.embed(...) call rather than the resolved value. Widen a field to formae.Embedded only where embedding genuinely helps, typically free-form text the user authors (code, scripts, templates, user-data) that needs a value from another resource. For a field that is only ever a whole reference, formae.Resolvable remains the right type.

Using Resolvables in Forma Files

Users reference resolvables through the resource’s res property:
When evaluated, the vpcId property contains a resolvable marker that formae resolves at apply time.

How Resolution Works

formae uses resolvables to build a dependency graph that determines the order of operations. If a Subnet references a VPC’s ID, formae knows to create the VPC first and destroy the Subnet first. During apply, formae creates the VPC, waits for the response containing {"VpcId": "vpc-12345"}, then passes that resolved value to the Subnet’s Create call. Your plugin never sees resolvable markers, just the final {"VpcId": "vpc-12345", "CidrBlock": "10.0.1.0/24"}. Resources without dependencies on each other can be processed in parallel. Note: Parent-child relationships (covered separately) are about discovery ordering. When formae discovers child resources, it automatically injects resolvables that feed into this same dependency graph.

Multiple Resolvable Properties

A resource can expose multiple resolvable properties:
Users can then reference any of these:

Resolvables in Collections

Resolvables work within collections:

Collection Resolvables

Some resource properties resolve to collections (Mappings or Listings) rather than scalar values. For example, a Docker Compose stack exposes an endpoints Mapping, and an OVH database service exposes an endpoints Listing. To let users reference individual items within these collections, use MappingResolvable or ListingResolvable instead of the base Resolvable:

Mapping Properties

Use MappingResolvable when a property resolves to a key-value map:
Users access individual values with at(key):
Special characters in keys (colons, dots) are escaped automatically.

Listing Properties

Use ListingResolvable when a property resolves to an ordered list:
Users access elements with at(index), which returns a MappingResolvable for further field navigation:

Typed Element Resolvables

For list properties whose elements have a known structure, you can create a typed resolvable for the element. This gives users field-level access instead of the generic at(key):
Users get ergonomic field access:
This pattern is opt-in. The generic MappingResolvable.at(key) fallback always works. Use typed element resolvables when your users frequently reference specific fields in list items.

Return Type Guidelines

Use the correct resolvable type for each property to get compile-time safety:

What Plugin Authors Need to Know

You don’t handle resolution. formae’s resolver handles all resolution logic. Your plugin:
  1. Defines which properties can be referenced (via the Resolvable class)
  2. Accepts union types for properties that might receive references
  3. Returns property values in Read/Create responses that formae uses to resolve references
The resolution machinery is entirely in formae’s core. Plugins just define the schema and implement CRUD operations.

Summary

Start by identifying which resource properties other resources might need to reference. Those are your resolvable candidates.