> ## Documentation Index
> Fetch the complete documentation index at: https://docs.formae.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Resolvable

> How you reference another resource's properties or a secret without knowing its value at write time.

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.

<Note>
  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](/documentation/get-started/write-your-first-forma) for the full pattern.
</Note>

## 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

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
local vpc = new vpc.VPC {
  label = "main-vpc"
  cidrBlock = "10.0.0.0/16"
}
vpc

local subnet1 = new subnet.Subnet {
  label = "subnet-1"
  vpcId = vpc.res.vpcId  // Reference the VPC's ID using .res
  cidrBlock = "10.0.1.0/24"
  availabilityZone = "us-west-2a"
}
subnet1

new dbsubnetgroup.DBSubnetGroup {
  label = "db-subnet-group"
  dbSubnetGroupDescription = "Subnet group for RDS"
  subnetIds {
    subnet1.res.subnetId  // Reference the subnet's ID
  }
}
```

## 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.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
// Create a secret with a randomly generated password
local dbSecret = new secret.Secret {
  label = "db-password"
  name = "my-db-password"
  description = "Database password secret"
  secretString = formae.value(random.password(12, false)).opaque.setOnce
}
dbSecret

// Reference the secret in a database instance
new dbinstance.DBInstance {
  label = "my-database"
  allocatedStorage = 20
  dbInstanceClass = "db.t3.micro"
  engine = "postgres"
  masterUsername = "admin"
  masterUserPassword = dbSecret.res.secretString  // Reference the secret using .res
}
```

* `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](/documentation/concepts/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:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
import "@aws/cloudfront/cffunction.pkl" as cffn
import "@aws/cloudfront/keyvaluestore.pkl" as kvsmod

// A Key Value Store's Id is generated by AWS at create time, so it is not
// known until after the store exists.
local store = new kvsmod.KeyValueStore {
  label = "feature-flags"
  name = "feature-flags"
}
store

new cffn.Function {
  label = "rewrite"
  name = "rewrite"
  autoPublish = true
  functionConfig = new cffn.FunctionConfig {
    runtime = "cloudfront-js-2.0"
    comment = "Reads from the key value store"
    keyValueStoreAssociations = new Listing {
      new cffn.KeyValueStoreAssociation {
        keyValueStoreARN = store.res.arn
      }
    }
  }
  // The store's Id is embedded directly in the function source.
  functionCode = formae.embed("""
    import cf from 'cloudfront';
    const kvsId = '\(store.res.id)';
    async function handler(event) {
      const kv = cf.kvs(kvsId);
      return event.request;
    }
    """)
}
```

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](/documentation/concepts/target#target-resolvables). This enables cross-plugin patterns where one plugin provides infrastructure and another plugin's target resolves its connection details from it:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
// Compose stack exposes endpoints as a Mapping
local lgtmStack = new compose.Stack {
  label = "lgtm"
  projectName = "formae-observability"
  composeFile = "..."
}

// Grafana target resolves a specific endpoint using at()
new formae.Target {
  label = "grafana"
  namespace = "GRAFANA"
  config = new grafana.Config {
    url = lgtmStack.res.endpoints.at("lgtm:3000")
  }
}
```

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](/documentation/concepts/target#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:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
// Map key access, Docker Compose endpoints
url = composeStack.res.endpoints.at("grafana:3000")

// List index + field access, OVH database endpoints
uri = dbService.res.endpoints.at(0).uri
```

See the Collection Resolvables section of the plugin SDK docs for details on defining these in your schemas.

## See also

* [Values](/documentation/concepts/values): the `.opaque` and `.setOnce` modifiers used with secrets.
* [Target](/documentation/concepts/target): target resolvables and cross-plugin connection resolution.
* [Properties](/documentation/concepts/properties): the CLI-facing counterpart to resolvables.
