Skip to content

How commands work

Not everything a deploy needs happens in a cloud. A frontend has to be built before it’s uploaded. Code generation has to run before a bundle that imports it. A dev server has to be running while you develop. Command puts those local processes into your Stack, as resources, so they run in the right order and only when they need to.

A build runs before the site that uploads its output. An exec runs before what depends on its effects. A dev server runs only under alchemy dev.

vite-buildCommand.Buildnpm run buildSiteuploads dist/upload dist/codegenCommand.ExecgenerateApiimports the codebundle and deployalchemy deploy
Whatever uses a command's result waits for it, and independent commands run at the same time. A dev server only runs under alchemy dev.

A build runs a command that writes an output directory, like npm run build writing dist. The directory is the build’s result, and other resources take it as an input:

alchemy.run.ts
const build = yield* Command.Build("vite-build", { command: "npm run build", cwd: "./frontend", outdir: "dist" });

The output’s hash is recorded too, so whatever uploads it knows when the files actually changed.

An exec runs a command for what it does, not what it writes: generate code, seed a database, call a script. It succeeds when the command exits with code 0.

alchemy.run.ts
yield* Command.Exec("codegen", { command: "npm run codegen", cwd: "./packages/api" });

Destroying an exec doesn’t undo what it did.

A dev server is a long-running process, like vite dev, that runs for as long as alchemy dev does. Its url is read from the process’s output, and other resources can use it. During alchemy deploy, it does nothing.

See Dev servers.

A build or exec runs again only when something it depends on changed. Its inputs, the files under its working directory, are hashed, along with its command and environment. Unchanged inputs mean no run, so a deploy that touched only the backend never rebuilds the frontend.

See Memoization.

Commands are resources with no cloud and no credentials. Command.providers() is already part of AWS.providers() and Cloudflare.providers(), and the Website resources on every cloud use builds and dev servers underneath. Declare one yourself for any local step the deploy depends on.