Skip to content

Command reference

Source: src/Command/Build.ts

A Build runs a shell command that produces an output asset (a file or directory) and tracks that asset in state. Unlike Exec, a Build has an output contract: reconcile verifies the command actually produced outdir and exposes its location so downstream resources (e.g. a Cloudflare.Worker’s static assets) can consume it.

Inputs are content-hashed by default so an unchanged project skips the rebuild entirely; set memo: false to rebuild on every deploy.

const build = yield* Build("vite-build", {
command: "npm run build",
cwd: "./frontend",
outdir: "dist",
});
yield* Console.log(build.outdir); // path to the dist directory, relative to the initial cwd
yield* Console.log(build.hash.output); // hash of the output files (when memo is enabled)
const build = yield* Build("production-build", {
command: "npm run build",
cwd: "./app",
outdir: "dist",
env: {
NODE_ENV: "production",
API_URL: "https://api.example.com",
},
});
const build = yield* Build("custom-build", {
command: "npm run build",
cwd: "./app",
outdir: "dist",
memo: { include: ["src/**", "package.json"], exclude: ["node_modules", "dist"] },
});

Source: src/Command/Dev.ts

A long-lived shell process scoped to a stack instance, started during alchemy dev and restarted when its inputs change. During alchemy deploy this is a no-op — Dev resources only run in dev mode.

The child process runs inside the dev sidecar (see Command/Local.ts) so it survives user-code HMR — Alchemy’s user process can restart without killing your npm run dev server. Its stdout/stderr are mirrored to the terminal (preserving colored output) and scanned for an http(s)://… URL, favoring a localhost/IP URL (the dev server’s own address) over any unrelated URL the command prints first. The result is exposed as the url output attribute — useful for surfacing a dev server’s local URL back out to whatever resource declared this Dev.

Pass a shell command that starts a long-lived dev server. Alchemy runs it in the background and extracts the first URL it prints.

const dev = yield* Dev("Frontend", {
command: "npm run dev",
});
yield* Console.log(dev.url); // e.g. "http://localhost:5173"

Use cwd to run the command in a subdirectory — useful in monorepos where each package has its own dev server.

const dev = yield* Dev("Web", {
command: "npm run dev",
cwd: "apps/web",
});

Extra environment variables are merged on top of process.env. Sensitive values can be wrapped in Redacted to keep them out of logs and state files.

const dev = yield* Dev("Api", {
command: "npm run dev",
env: {
PORT: "4000",
DATABASE_URL: Redacted.make("postgres://..."),
},
});

Source: src/Command/Exec.ts

An Exec runs a shell command purely for its side effects — it has no output contract. Unlike Build, it does not produce or track an output asset; reconcile runs the command and the resource succeeds as long as the command exits with code 0 (a non-zero exit fails with a CommandError).

Use it for one-off setup steps — running migrations, seeding data, code generation, or any command whose result lives outside Alchemy’s state. By default the input files are content-hashed so the command only re-runs when its inputs (or command/cwd/env) change; set memo: false to re-run on every deploy.

yield* Exec("codegen", {
command: "npm run codegen",
cwd: "./packages/api",
});
yield* Exec("migrate", {
command: "npm run db:migrate",
env: {
DATABASE_URL: Redacted.make("postgres://..."),
},
});
yield* Exec("codegen", {
command: "npm run codegen",
memo: { include: ["schema/**"] },
});
yield* Exec("migrate", {
command: "npm run db:migrate",
timeout: "5 minutes",
});