Architecture
TerraDart is a thin authoring layer that produces standard Terraform JSON. The Dart side gives you types, refactor, and dart analyze; the Terraform side keeps state, planning, and apply unchanged.
Pipeline
From Dart to terraform apply
Author
Write final class XxxStack extends Stack using curated factories from provider packages.
Generate Terraform JSON
Call stack.writeTo('tf-out') to emit *.tf.json, or stack.synth() first if you need the in-memory SynthResult.
Apply
Run terraform plan and terraform apply against your existing state backend.
Hand off (optional)
Surface AppExport values as typed Dart constants and Terraform outputs for downstream apps.
What is synth?
Section titled “What is synth?”On the landing page, we say you generate *.tf.json — plain language for the outcome.
In the API, synth is the in-memory step: your Stack is walked and assembled into the Terraform JSON tree. Code: stack.synth() → SynthResult. Writing files is the next step: stack.writeTo(outDir).
Stack lifecycle (synth() internals)
Section titled “Stack lifecycle (synth() internals)”graph LR S[Stack instance] --> SY["synth()"] SY --> SR[SynthResult] SR --> W["writeTo(outDir)"] W --> TF[main.tf.json] W -.optional.-> AD[".app.dart constants"]
synth() is the pure, in-memory step. It walks the resources you registered with add(...), applies lifecycle wiring, dedups, and produces a SynthResult whose tfJson field carries the Terraform JSON tree.
writeTo(outDir) is the file-I/O wrapper. It always writes main.tf.json. If you registered AppExports via addExport(...) AND called setAppExportsOutputPath(...), it also writes a generated .app.dart file with typed Dart constants. If you registered exports but forgot to set the output path, writeTo throws StateError before writing anything — the failure mode is atomic, never partial.
Stack, Resource, and Data are abstract base class. Your subclasses must declare a class modifier:
final class AppInfraStack extends Stack { /* ... */ }base and sealed are also valid; what is rejected is plain class (or implements Stack, which would bypass the base-class state that synth depends on).
Provider integration
Section titled “Provider integration”TerraDart supports multiple official Terraform providers with curated, typed Dart factories:
terradart_google— GA HashiCorpgoogleprovider catalog. See status and the package README for counts; the full list is on Coverage.terradart_google_beta— beta-only types fromhashicorp/google-beta(128 resource factories at the current pin).terradart_appwrite— officialappwrite/appwriteprovider resources (filled at the current pin).terradart_cloudflare— officialcloudflare/cloudflareprovider resources (filled at5.23.0: 257 resource factories + 446 data sources; nested plugin-framework objects are typed Dart helpers).
For the Google provider, TerraDart depends on upstream sources reconciled automatically on a weekly cadence:
graph LR MM[Magic Modules repo] MM -->|MM generator| TPG[terraform-provider-google] MM -->|MM YAML overlay| MMY["mm/*.yaml fixtures"] TPG -->|"terraform providers schema -json"| SJ[schema.json fixture] SJ --> CG[terradart_codegen] MMY --> CG CG --> WR["terradart_google wrappers"]
The MM YAML overlay carries metadata that the provider schema does not surface — sensitive_fields, force_new, free-form description text. terradart_codegen merges inputs into typed <resource>.schema.dart files that compose into the terradart_google package.
Provider tracking for Google is pinned to GA ~> 7.0. Beta-only types live in terradart_google_beta (128 resource factories at the current pin); overlapping GA names stay in terradart_google.
A weekly GitHub Actions workflow (.github/workflows/schema-bump.yml) runs every Monday morning JST. It detects new terraform-provider-google v7 releases and MM YAML overlay changes, refreshes the local schema + MM fixtures, runs terradart wrap --check, and opens a single drift-report PR if anything needs maintainer attention. Quiet weeks produce no PR.
You don’t need to track this loop to use TerraDart. The reason it’s documented here is so that, when a v0.x.x release adds new curated factories or shifts an existing one, you can see exactly which upstream change drove it.
AppExport: the IaC ↔ application seam
Section titled “AppExport: the IaC ↔ application seam”AppExport is the typed handoff from your Stack to whatever Dart application code consumes the resources it provisions (for example Cloud Run functions handlers).
graph LR AE["addExport(name, AppExport)"] AE --> TFO["Terraform output block (main.tf.json)"] AE --> DC[".app.dart const class"] ST[Application code] --> DC TF[Terraform CLI] --> TFO
Two export kinds cover most cases:
ResourceIdExport— for values that aren’t known until apply time, like a Cloud Run service URL. PassemitTerraformOutput: trueand Terraform writes the resolved value into its output. Application code can fetch it viaterraform output -raw <name>.StringExport— for literals known at synth time (service name, region, project ID). These materialize asstatic constdeclarations on the generated<StackName>Exportsclass in your.app.dartfile —dart analyzecatches rename drift before you everterraform apply.
Calling setAppExportsOutputPath(...) is what triggers the .app.dart emission. Exports whose values are only known after apply (for example ResourceIdExport(topic.id)) emit Terraform output blocks only; literal-resolvable exports (for example ResourceIdExport(topic.nameRef)) become typed Dart constants. Runnable pattern: pubsub quickstart (lib/subscriber_stub.dart).
A worked end-to-end example lives in the cookbook single-project-app recipe, exporting coffee_service_uri (Terraform output), SERVICE_NAME, and REGION (Dart constants).
Non-goals
Section titled “Non-goals”- Not a Terraform replacement — state and apply stay in Terraform.
- Not a multi-cloud abstraction layer — wrappers faithfully mirror provider schemas rather than imposing cross-cloud abstractions.
- Not a constructs framework in the pre-1.0 cycle.
- Not module-block support — compose Terraform modules in HCL alongside TerraDart-generated
*.tf.json; both feed the sameterraform apply.
See README — Non-goals for the canonical list.
Further reading
Section titled “Further reading”- How it’s built — the generation pipeline and verification harness behind these factories.
terradart_coreon pub.dev — Stack / TfArg / AppExport API reference.terradart_googleon pub.dev — the curated Google factory catalog.terradart_appwriteon pub.dev — curated Appwrite factories.terradart_cloudflareon pub.dev — curated Cloudflare factories.terradart_codegenon pub.dev —wrap/codegenCLI reference.examples/on GitHub — runnable quickstart Stacks.