Skip to main content

Creating Resources

Alchemy uses a pseudo-class pattern for defining infrastructure resources. Each resource is created using the Resource() function, which manages the complete lifecycle (create, update, delete) of your infrastructure.

Basic Resource Creation

Resources are created by calling a resource constructor function with an ID and props:
import alchemy from "alchemy";
import { Worker } from "alchemy/cloudflare";

const app = await alchemy("my-app");

const worker = await Worker("api", {
  entrypoint: "./src/index.ts",
  bindings: {
    API_KEY: alchemy.secret.env.API_KEY
  }
});

await app.finalize();
1
Initialize an Alchemy Application
2
Every Alchemy script starts by creating an application scope:
3
const app = await alchemy("my-app");
4
This creates a scope that:
5
  • Manages resource state in .alchemy/
  • Automatically parses CLI arguments (--destroy, --stage, etc.)
  • Tracks all resources created within the scope
  • 6
    Create Resources
    7
    Resources are created using provider-specific constructors. Each resource requires:
    8
  • ID: A unique identifier within your scope (e.g., "api", "database")
  • Props: Configuration properties specific to the resource type
  • 9
    const bucket = await R2Bucket("storage", {
      name: "my-bucket"
    });
    
    const database = await D1Database("db", {
      name: "my-database"
    });
    
    10
    Finalize the Scope
    11
    Always call finalize() to complete the deployment:
    12
    await app.finalize();
    
    13
    This triggers:
    14
  • Resource creation/updates
  • Cleanup of orphaned resources
  • State persistence
  • Resource IDs and Physical Names

    Every resource has two important identifiers:

    Resource ID

    The ID is the logical identifier used in your Alchemy code:
    const worker = await Worker("api", { /* ... */ });
    //                            ^^^^^ Resource ID
    
    • Must be unique within a scope
    • Used to reference the resource in state
    • Cannot contain colons (:)

    Physical Name

    The physical name is the actual name in the cloud provider. By default, Alchemy generates this as:
    {app}-{stage}-{id}
    
    const app = await alchemy("my-app");
    // stage defaults to your username or $USER
    
    const worker = await Worker("api", {
      entrypoint: "./src/index.ts"
    });
    // Physical name: "my-app-john-api"
    
    Use custom names when you need to reference existing resources or follow specific naming conventions.

    Resource Lifecycle

    Alchemy automatically manages the complete lifecycle of your resources:

    Create Phase

    When you run your alchemy.run.ts script, new resources are created:
    const worker = await Worker("api", {
      entrypoint: "./src/index.ts"
    });
    // Output: Create Worker "my-app-john-api"
    

    Update Phase

    If you change resource properties and re-run, Alchemy updates the resource:
    const worker = await Worker("api", {
      entrypoint: "./src/index.ts",
      bindings: {
        DATABASE: database  // Added binding
      }
    });
    // Output: Update Worker "my-app-john-api"
    

    Delete Phase

    Run with --destroy to delete all resources:
    bun ./alchemy.run.ts --destroy
    
    Alchemy tracks which resources exist in your code. If you remove a resource from your script, it will be automatically deleted on the next run (orphan cleanup).

    Resource References

    You can pass resources as properties to other resources:
    const database = await D1Database("db", {
      name: "my-database"
    });
    
    const bucket = await R2Bucket("storage", {
      name: "my-bucket"
    });
    
    const worker = await Worker("api", {
      entrypoint: "./src/index.ts",
      bindings: {
        DB: database,      // Pass D1Database resource
        BUCKET: bucket     // Pass R2Bucket resource
      }
    });
    
    Alchemy automatically:
    • Resolves resource dependencies
    • Creates resources in the correct order
    • Extracts the necessary properties for bindings

    Concurrent Resource Creation

    Resources can be created concurrently when they don’t depend on each other:
    const [worker, bucket, database] = await Promise.all([
      Worker("api", { entrypoint: "./src/worker.ts" }),
      R2Bucket("storage", { name: "my-bucket" }),
      D1Database("db", { name: "my-db" })
    ]);
    
    Use Promise.all() to create independent resources faster. Keep batches under 50 resources for optimal performance.

    Adopting Existing Resources

    If a resource already exists with the same name, you can adopt it:
    const app = await alchemy("my-app", {
      adopt: true  // Enable adoption globally
    });
    
    const worker = await Worker("api", {
      name: "existing-worker",
      entrypoint: "./src/index.ts"
    });
    // If "existing-worker" exists, it will be adopted and updated
    
    Or per-resource:
    const worker = await Worker("api", {
      name: "existing-worker",
      entrypoint: "./src/index.ts",
      adopt: true  // Enable adoption for this resource only
    });
    
    Adoption will update the existing resource to match your configuration. Make sure this is intentional before enabling.

    Resource Outputs

    Every resource returns output properties you can use:
    const worker = await Worker("api", {
      entrypoint: "./src/index.ts"
    });
    
    console.log(worker.url);  // https://my-app-john-api.account.workers.dev
    console.log(worker.id);   // "api"
    console.log(worker.name); // "my-app-john-api"
    
    These outputs can be used to:
    • Display deployment information
    • Configure other resources
    • Pass to external systems

    Error Handling

    Alchemy provides clear error messages for common issues:
    try {
      const worker = await Worker("api", {
        entrypoint: "./src/index.ts"
      });
    } catch (error) {
      console.error("Failed to create worker:", error.message);
    }
    
    Common errors:
    • Duplicate resource ID: Using the same ID twice in a scope
    • Invalid resource ID: IDs containing colons or invalid characters
    • Resource conflicts: Resource already exists without adopt: true
    • Missing credentials: Provider credentials not configured

    Best Practices

    1
    Use Descriptive IDs
    2
    // Good
    const apiWorker = await Worker("api", { /* ... */ });
    const userDb = await D1Database("user-db", { /* ... */ });
    
    // Avoid
    const w1 = await Worker("w1", { /* ... */ });
    const db = await D1Database("db", { /* ... */ });
    
    4
    // API Infrastructure
    const apiDb = await D1Database("api-db", { /* ... */ });
    const apiWorker = await Worker("api", {
      bindings: { DB: apiDb }
    });
    
    // Frontend Infrastructure
    const assets = await R2Bucket("assets", { /* ... */ });
    const frontend = await Vite("frontend", {
      bindings: { ASSETS: assets }
    });
    
    5
    Always Call finalize()
    6
    Ensure cleanup and orphan removal:
    7
    const app = await alchemy("my-app");
    
    try {
      // Create resources...
    } finally {
      await app.finalize();
    }
    

    Next Steps

    Build docs developers (and LLMs) love