Skip to main content

Resources

Resources are the fundamental building blocks in Alchemy. Each resource represents a piece of infrastructure—whether it’s a worker, database, storage bucket, or any other cloud service component.

What is a Resource?

A resource in Alchemy is a TypeScript function that manages the complete lifecycle of an infrastructure component. Resources handle:
  • Creation: Provisioning new infrastructure
  • Updates: Applying configuration changes
  • Deletion: Cleaning up resources when no longer needed
  • State tracking: Maintaining resource state across deployments
All resources implement the same lifecycle pattern, making them composable and predictable regardless of the underlying provider.

Resource Anatomy

Resources are defined using the Resource() function and follow a consistent structure:
import { Resource } from "alchemy";
import type { Context } from "alchemy";

export interface MyResourceProps {
  /**
   * Name of the resource
   * @default ${app}-${stage}-${id}
   */
  name?: string;
  
  /**
   * Configuration property
   */
  property: string;
}

export interface MyResource extends Resource<"provider::MyResource"> {
  id: string;
  name: string;
  property: string;
}

export const MyResource = Resource(
  "provider::MyResource",
  async function (
    this: Context<MyResource>,
    id: string,
    props: MyResourceProps
  ): Promise<MyResource> {
    // Handle deletion
    if (this.phase === "delete") {
      // Clean up the resource
      return this.destroy();
    }

    // Create or update the resource
    return {
      id,
      name: props.name ?? this.scope.createPhysicalName(id),
      property: props.property,
    };
  }
);

Resource Identity

Every resource has multiple forms of identity:

Logical ID

The first parameter passed to a resource is its logical ID—a unique identifier within your application:
const worker = await Worker("api", {
  entrypoint: "./src/api.ts"
});
Here, "api" is the logical ID.
Changing a resource’s logical ID creates a new resource and deletes the old one. This can lead to data loss for stateful resources.

Fully Qualified Name (FQN)

The FQN is the complete path to a resource, including the app name, scopes, and logical ID:
my-app/dev/api
my-app/prod/database/primary
Access via resource[ResourceFQN]:
import { ResourceFQN } from "alchemy";

console.log(worker[ResourceFQN]); // "my-app/dev/api"

Physical Name

The physical name is what appears in your cloud provider’s console. Alchemy generates deterministic names by default:
const name = this.scope.createPhysicalName(id);
// Returns: "my-app-api-dev"
Physical names are designed to be human-readable while avoiding conflicts across stages and applications.

Resource Lifecycle

Resources go through distinct phases during deployment:

Create Phase

When a resource doesn’t exist in state:
if (this.phase === "create") {
  // this.output is undefined
  // Create brand new infrastructure
  const result = await api.create(props);
  return result;
}

Update Phase

When a resource exists but props have changed:
if (this.phase === "update") {
  // this.output contains previous state
  // this.props contains previous props
  const result = await api.update(this.output.id, props);
  return result;
}

Delete Phase

When a resource is removed from your code:
if (this.phase === "delete") {
  // Clean up infrastructure
  await api.delete(this.output.id);
  return this.destroy();
}
The this.destroy() call is required in the delete phase—it signals Alchemy that cleanup is complete and state should be removed.

Resource Context

The this context in a resource handler provides access to:
PropertyTypeDescription
this.phase"create" | "update" | "delete"Current lifecycle phase
this.outputResource | undefinedPrevious resource state (undefined in create)
this.propsProps | undefinedPrevious props (undefined in create)
this.scopeScopeCurrent scope with app/stage info
this.idstringLogical ID of the resource
this.fqnstringFully qualified name
this.get(key)Promise<T>Get custom state data
this.set(key, value)Promise<void>Set custom state data
this.replace()neverSignal resource replacement
this.destroy()neverComplete deletion

Resource Properties

Every Alchemy resource has these built-in properties:
import { ResourceKind, ResourceID, ResourceFQN } from "alchemy";

const worker = await Worker("api", { ... });

console.log(worker[ResourceKind]);  // "cloudflare::Worker"
console.log(worker[ResourceID]);    // "api"
console.log(worker[ResourceFQN]);   // "my-app/dev/api"
These symbols are used internally for type discrimination and resource tracking. You typically don’t need to access them directly.

Resource Dependencies

Resources can reference each other, creating automatic dependency graphs:
const database = await D1Database("db", {
  name: "my-database"
});

const worker = await Worker("api", {
  entrypoint: "./src/api.ts",
  bindings: {
    DB: database  // Worker depends on database
  }
});
Alchemy automatically:
  • Creates resources in dependency order
  • Updates dependent resources when dependencies change
  • Deletes resources in reverse dependency order

Resource Replacement

Some property changes require replacing a resource:
export const MyResource = Resource(
  "provider::MyResource",
  async function (this: Context<MyResource>, id: string, props: MyResourceProps) {
    if (this.phase === "update" && this.output.name !== props.name) {
      // Name is immutable - trigger replacement
      return this.replace();
    }

    // ... rest of implementation
  }
);
1

Alchemy creates the new resource

A new resource is created with the updated configuration
2

Alchemy deletes the old resource

Once the new resource is ready, the old one is destroyed
3

State is updated

State files are updated to track the new resource
Replacement causes downtime for the resource. Plan accordingly for production systems.

Type Safety

Alchemy provides full TypeScript type safety:
const worker = await Worker("api", {
  entrypoint: "./src/api.ts",
  bindings: {
    // TypeScript knows DB must be a D1Database
    DB: database
  }
});

// TypeScript knows worker.url is a string
console.log(worker.url);

// TypeScript error: Property 'invalid' does not exist
console.log(worker.invalid);

Custom State

Resources can store custom data in state:
export const MyResource = Resource(
  "provider::MyResource",
  async function (this: Context<MyResource>, id: string, props: MyResourceProps) {
    // Store custom data
    await this.set("deploymentCount", 
      (await this.get<number>("deploymentCount") ?? 0) + 1
    );

    const count = await this.get<number>("deploymentCount");
    console.log(`Deployed ${count} times`);

    // ... rest of implementation
  }
);
Custom state is useful for tracking metadata that doesn’t belong in the resource’s output but needs to persist across deployments.

Local Development

Resources can provide local alternatives for development:
export const MyResource = Resource(
  "provider::MyResource",
  async function (this: Context<MyResource>, id: string, props: MyResourceProps) {
    if (this.scope.local) {
      // Return mock/local version
      return {
        id,
        name: props.name || id,
        url: "http://localhost:8787"
      };
    }

    // Production implementation
    // ...
  }
);
Run with local mode:
bun ./alchemy.run.ts --local

Resource Registration

Resources are automatically registered in a global registry when defined:
import { PROVIDERS } from "alchemy";

// Get a provider by type
const provider = PROVIDERS.get("cloudflare::Worker");
The global registry enables Alchemy to resolve resources during deletion even if the code has been removed.

Best Practices

1

Use descriptive logical IDs

Choose IDs that clearly describe the resource’s purpose: "api", "user-db", "image-bucket"
2

Handle all phases

Always implement create, update, and delete logic comprehensively
3

Validate immutable properties

Detect immutable property changes and call this.replace() when needed
4

Provide defaults

Use this.scope.createPhysicalName(id) for default naming
5

Support local development

Check this.scope.local and provide local alternatives when possible

Next Steps

Scopes

Learn how to organize resources with scopes

State Management

Understand how Alchemy tracks resource state

Secrets

Secure sensitive values in your infrastructure

Lifecycle

Deep dive into the resource lifecycle

Build docs developers (and LLMs) love