# API & Data Sources ## Outputs and Exports Export resource identifiers: ```typescript export const kvId = kv.id; export const bucketName = bucket.name; export const workerUrl = worker.subdomain; export const dbId = db.id; ``` ## Resource Dependencies Implicit dependencies via outputs: ```typescript const kv = new cloudflare.WorkersKvNamespace("kv", { accountId: accountId, title: "my-kv", }); // Worker depends on KV (implicit via kv.id) const worker = new cloudflare.WorkerScript("worker", { accountId: accountId, name: "my-worker", content: code, kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}], // Creates dependency }); ``` Explicit dependencies: ```typescript const migration = new command.local.Command("migration", { create: pulumi.interpolate`wrangler d1 execute ${db.name} --file ./schema.sql`, }, {dependsOn: [db]}); const worker = new cloudflare.WorkerScript("worker", { accountId: accountId, name: "worker", content: code, d1DatabaseBindings: [{name: "DB", databaseId: db.id}], }, {dependsOn: [migration]}); // Ensure migrations run first ``` ## Using Outputs with API Calls ```typescript const db = new cloudflare.D1Database("db", {accountId, name: "my-db"}); db.id.apply(async (dbId) => { const response = await fetch( `https://api.cloudflare.com/client/v4/accounts/${accountId}/d1/database/${dbId}/query`, {method: "POST", headers: {"Authorization": `Bearer ${apiToken}`, "Content-Type": "application/json"}, body: JSON.stringify({sql: "CREATE TABLE users (id INT)"})} ); return response.json(); }); ``` ## Custom Dynamic Providers For resources not in provider: ```typescript import * as pulumi from "@pulumi/pulumi"; class D1MigrationProvider implements pulumi.dynamic.ResourceProvider { async create(inputs: any): Promise { const response = await fetch( `https://api.cloudflare.com/client/v4/accounts/${inputs.accountId}/d1/database/${inputs.databaseId}/query`, {method: "POST", headers: {"Authorization": `Bearer ${inputs.apiToken}`, "Content-Type": "application/json"}, body: JSON.stringify({sql: inputs.sql})} ); return {id: `${inputs.databaseId}-${Date.now()}`, outs: await response.json()}; } async update(id: string, olds: any, news: any): Promise { if (olds.sql !== news.sql) await this.create(news); return {}; } async delete(id: string, props: any): Promise {} } class D1Migration extends pulumi.dynamic.Resource { constructor(name: string, args: any, opts?: pulumi.CustomResourceOptions) { super(new D1MigrationProvider(), name, args, opts); } } const migration = new D1Migration("migration", { accountId, databaseId: db.id, apiToken, sql: "CREATE TABLE users (id INT)", }, {dependsOn: [db]}); ``` ## Data Sources **Get Zone:** ```typescript const zone = cloudflare.getZone({name: "example.com"}); const zoneId = zone.then(z => z.id); ``` **Get Accounts (via API):** Use Cloudflare API directly or custom dynamic resources. ## Import Existing Resources ```bash # Import worker pulumi import cloudflare:index/workerScript:WorkerScript my-worker / # Import KV namespace pulumi import cloudflare:index/workersKvNamespace:WorkersKvNamespace my-kv # Import R2 bucket pulumi import cloudflare:index/r2Bucket:R2Bucket my-bucket / # Import D1 database pulumi import cloudflare:index/d1Database:D1Database my-db / # Import DNS record pulumi import cloudflare:index/dnsRecord:DnsRecord my-record / ``` ## Secrets Management ```typescript import * as pulumi from "@pulumi/pulumi"; const config = new pulumi.Config(); const apiKey = config.requireSecret("apiKey"); // Encrypted in state const worker = new cloudflare.WorkerScript("worker", { accountId: accountId, name: "my-worker", content: code, secretTextBindings: [{name: "API_KEY", text: apiKey}], }); ``` Store secrets: ```bash pulumi config set --secret apiKey "secret-value" ``` ## Transform Pattern Modify resource args before creation: ```typescript import {Transform} from "@pulumi/pulumi"; interface BucketArgs { accountId: pulumi.Input; transform?: {bucket?: Transform}; } function createBucket(name: string, args: BucketArgs) { const bucketArgs: cloudflare.R2BucketArgs = { accountId: args.accountId, name: name, location: "auto", }; const finalArgs = args.transform?.bucket?.(bucketArgs) ?? bucketArgs; return new cloudflare.R2Bucket(name, finalArgs); } ``` ## v6.x Worker Versioning Resources **Worker** - Container for versions: ```typescript const worker = new cloudflare.Worker("api", {accountId, name: "api-worker"}); export const workerId = worker.id; ``` **WorkerVersion** - Immutable code + config: ```typescript const version = new cloudflare.WorkerVersion("v1", { accountId, workerId: worker.id, content: fs.readFileSync("./dist/worker.js", "utf8"), compatibilityDate: "2025-01-01", }); export const versionId = version.id; ``` **WorkersDeployment** - Active deployment with bindings: ```typescript const deployment = new cloudflare.WorkersDeployment("prod", { accountId, workerId: worker.id, versionId: version.id, kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}], }); ``` **Use:** Advanced deployments (canary, blue-green). Most apps should use `WorkerScript` (auto-versioning). --- See: [README.md](./README.md), [configuration.md](./configuration.md), [patterns.md](./patterns.md), [gotchas.md](./gotchas.md)