Overview
Alchemy is a TypeScript library that creates and manages cloud infrastructure when you run it. Instead of using a CLI or configuration files, you write a regular TypeScript program.
How it works
Section titled “How it works”You start with an alchemy.run.ts
file (or any other name you want) that contains your infrastructure code:
import alchemy from "alchemy";import { Worker } from "alchemy/cloudflare";
// Create an appconst app = await alchemy("my-app");
// Create resourcesconst worker = await Worker("api", { entrypoint: "./src/api.ts"});
// Clean up orphaned resourcesawait app.finalize();
Alchemy doesn’t have a traditional CLI tool like wrangler
or terraform
. Instead, it automatically parses CLI arguments when you run your TypeScript scripts:
bun ./alchemy.run.ts # deploy to cloudbun ./alchemy.run.ts --destroy # tear down (destroy all resources)bun ./alchemy.run.ts --read # read-only modebun ./alchemy.run.ts --stage prod # deploy to specific stage
# local dev & hot redeploymentbun --watch ./alchemy.run.ts # hot redeployment to cloudbun --watch ./alchemy.run.ts --dev # local development with hot redeployment
Embeddable
Section titled “Embeddable”Since Alchemy is a TypeScript library, you can override any CLI arguments programmatically. Explicit options always take precedence over CLI arguments:
// CLI args are parsed automaticallyconst app = await alchemy("my-app");
// Override CLI args with explicit optionsconst app = await alchemy("my-app", { phase: "up", // Overrides --destroy or --read stage: "prod", // Overrides --stage quiet: false, // Overrides --quiet password: "secret", // Overrides ALCHEMY_PASSWORD env var dev: true, // Overrides --dev detection});
This makes Alchemy embeddable in larger applications where you need programmatic control over infrastructure deployment.
Resources
Section titled “Resources”Resources are async functions that create infrastructure. Each resource handles its own lifecycle:
// Create a KV namespaceconst kv = await KVNamespace("cache", { title: "My Cache"});
// Create a worker with the KV bindingconst worker = await Worker("api", { entrypoint: "./src/worker.ts", bindings: { CACHE: kv }});
By default, Alchemy tracks what it creates in .alchemy/
directory:
.alchemy/ my-app/ dev/ cache.json api.json
You can also use a remote state store like Durable Objects, R2, S3, etc. See State for more information.
Phases
Section titled “Phases”Your script can run in three phases:
- up (default) - Create, update, or delete resources
- read - Read existing resources without changes
- destroy - Delete all resources
const app = await alchemy("my-app", { phase: "destroy" // or pass --destroy flag});
Scopes
Section titled “Scopes”Resources are organized in scopes - like folders for your infrastructure:
const app = await alchemy("my-app"); // app scope
// Resources here are in the app/dev scopeconst db = await Database("main");
// Create a nested scopeawait alchemy.run("backend", async () => { // Resources here are in app/dev/backend scope const api = await Worker("api");});
Secrets
Section titled “Secrets”Alchemy provides built-in encryption for sensitive data like API keys, passwords, and credentials:
const app = await alchemy("my-app", { password: process.env.SECRET_PASSPHRASE, // Used to encrypt secrets});
// Create encrypted secrets from environment variablesconst apiKey = alchemy.secret(process.env.API_KEY);const databaseUrl = alchemy.secret(process.env.DATABASE_URL);
Secrets are automatically encrypted when stored in state files and can be safely used in your infrastructure.
Bindings
Section titled “Bindings”Connect resources together:
const kv = await KVNamespace("data");const queue = await Queue("tasks");
const worker = await Worker("processor", { entrypoint: "./processor.ts", bindings: { DATA: kv, // KV namespace binding TASKS: queue, // Queue binding API_KEY: alchemy.secret(process.env.API_KEY) // Secret }});
Local Development
Section titled “Local Development”Run locally with hot reloading:
bun ./alchemy.run.ts --dev
Resources can run locally or connect to remote services:
const db = await D1Database("app-db", { dev: { remote: true } // Use real D1 in dev mode});
Resource Adoption
Section titled “Resource Adoption”When creating a resource, Alchemy will fail if a resource with the same name already exists. You can opt in to adopt existing resources instead:
// Without adoption - fails if bucket already existsconst bucket = await R2Bucket("my-bucket", { name: "existing-bucket",});
// With adoption - uses existing bucket if it existsconst bucket = await R2Bucket("my-bucket", { name: "existing-bucket", adopt: true,});
This is useful when you want to manage existing infrastructure with Alchemy.
Resource Replacement
Section titled “Resource Replacement”Sometimes it’s impossible to update a resource (like renaming an R2 bucket). In these cases, Alchemy can replace the resource by:
- Creating a new version of the resource
- Updating all references to point to the new resource
- Deleting the old resource during finalization
// If you change immutable properties, Alchemy will automatically// trigger a replacement during the update phaseconst bucket = await R2Bucket("data", { name: "new-bucket-name" // This will trigger replacement});
The replacement happens seamlessly - downstream resources are updated to reference the new resource before the old one is deleted.
Custom Resources
Section titled “Custom Resources”All resources in Alchemy are equal - they’re just async functions that handle create, update, and delete operations. This means you can easily create your own resources for any service or API:
export const MyResource = Resource( "my-service::MyResource", async function(this: Context<MyResource>, id: string, props: MyResourceProps): Promise<MyResource> { if (this.phase === "delete") { // Delete logic return this.destroy(); } else if (this.phase === "update") { // Update logic return this({ ...props, id: this.output.id }); } else { // Create logic return this({ ...props, id: "new-id" }); } });
Resources handle their own lifecycle and can integrate with any API or service. All built-in Alchemy resources use the same pattern.
Testing
Section titled “Testing”Test your own custom resources with Alchemy’s built-in test helper that :
import { alchemy, destroy } from "alchemy";import "alchemy/test/vitest";
const test = alchemy.test(import.meta);
test("create worker", async (scope) => { try { const worker = await Worker("test-worker", { script: "export default { fetch() { return new Response('ok') } }" }); const response = await fetch(worker.url); expect(response.status).toBe(200); } finally { await destroy(scope); // Clean up test resources }});
Available Providers
Section titled “Available Providers”- Cloudflare: Worker, KV, R2, D1, Queue, Durable Objects
- AWS: S3, DynamoDB, Lambda, ECS, VPC
- Others: Docker, GitHub, Stripe, Vercel, Neon, PlanetScale
Next Steps
Section titled “Next Steps”- Getting Started - Deploy your first worker
- Concepts - Deep dive into how Alchemy works
- Guides - Build real applications
Happy transmutation! ✨