feat: 完成现有小程序接口的全部迁移至新后端

This commit is contained in:
R524809
2026-10-19 13:17:51 +08:00
parent 8827110f2f
commit d30cf25890
148 changed files with 13287 additions and 34 deletions
@@ -0,0 +1,189 @@
# app-composer-setup
Compute deploys only Prisma Composer apps: a root module, one service declaration per server, and a `composer` section in `prisma.config.ts`.
## Priority
CRITICAL
## Why It Matters
`prisma deploy` takes a module file, not a framework name or a config file. Without `module.ts`, a `compute()` service, and the `composer` config section, there is nothing to deploy. This file covers the minimum an agent needs. For contracts between services, input schemas, modules, and testing, use the `prisma-composer-core-concepts` skill and the [Composer docs](https://www.prisma.io/docs/composer).
## Requirements
- Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer (`engines.node` is `^22.18.0 || ^24.11.0 || >=26.0.0` since Composer 0.30.0). Composer loads the `.ts` entry directly; older versions stop at `ERR_UNKNOWN_FILE_EXTENSION`.
- Bun. Deployed services run on Bun, and `bun build --target=bun` is the simplest way to produce the single-file entry a service needs.
- The packages:
```bash
npm install @prisma/composer @prisma/composer-prisma-cloud
npm install -D prisma
npx prisma skills sync
```
`@prisma/composer` pins its own `effect` version; do not add a different `effect` to the app. See [`debug-deploy-failures.md`](debug-deploy-failures.md) if `prisma.config.ts` fails to evaluate.
## The Four Files
```ts
// src/server.ts
import service from "./service.ts";
Bun.serve({
port: service.port(),
hostname: "0.0.0.0",
fetch: () => new Response("ok"),
});
```
```ts
// src/service.ts
import node from "@prisma/composer/node";
import { compute } from "@prisma/composer-prisma-cloud";
export default compute({
name: "web",
deps: {},
build: node({ module: import.meta.url, entry: "../dist/server.mjs" }),
});
```
```ts
// module.ts
import { module } from "@prisma/composer";
import web from "./src/service.ts";
export default module("my-app", ({ provision }) => {
provision(web);
});
```
```ts
// prisma.config.ts
import { defineConfig as composer } from "@prisma/composer/config";
import { nodeBuild } from "@prisma/composer/node/control";
import { prismaCloud, prismaState } from "@prisma/composer-prisma-cloud/control";
import { definePrismaConfig } from "prisma/config";
export default definePrismaConfig({
composer: composer({
extensions: [prismaCloud({ region: "us-east-1" }), nodeBuild()],
state: prismaState(),
}),
});
```
And a build script in `package.json` that produces the file `entry` points at:
```json
{
"scripts": {
"build": "bun build src/server.ts --target=bun --outfile dist/server.mjs"
}
}
```
Rules:
- `entry` and `dir` resolve relative to the service module (`module: import.meta.url`), not the project root.
- The `composer` section accepts only `extensions` and `state`. Add `nextjsBuild()` from `@prisma/composer/nextjs/control` when a service uses the `nextjs` adapter.
- `prisma.config.ts` is read by the CLI only; app code never imports it. A separate `prisma-composer.config.ts` is refused.
- Inside the module graph, write relative imports with explicit `.ts` extensions and enable `allowImportingTsExtensions` in `tsconfig.json`.
## Region
The first deploy creates the project and needs a region. Set `prismaCloud({ region })` or `PRISMA_REGION`; the config value wins when both are set. Without either, the deploy stops with `project "<name>" does not exist yet and no deploy region is configured`. Once the project exists it keeps its region, and stages inherit it.
Region ids: `us-east-1`, `us-west-1`, `eu-west-3`, `eu-central-1`, `ap-northeast-1`, `ap-southeast-1`.
## Names
- The root module name (`module("my-app", ...)`) becomes the project name and may contain hyphens. `deploy --name <name>` overrides it.
- `compute({ name })` and `provision()` ids allow `[A-Za-z0-9]` only. A hyphenated id passes `tsc` and then fails at load. The platform rejects provision ids shorter than three characters, so name a database `database`, not `db`.
## Databases
Declare the database as a dependency of the service and provision it in the module that owns it. The service reads the value with `service.load()`.
```ts
// src/service.ts
import node from "@prisma/composer/node";
import { compute, rawPostgres } from "@prisma/composer-prisma-cloud";
export default compute({
name: "api",
deps: { db: rawPostgres() },
build: node({ module: import.meta.url, entry: "../dist/server.mjs" }),
});
```
```ts
// module.ts
import { module } from "@prisma/composer";
import { rawPostgres } from "@prisma/composer-prisma-cloud";
import api from "./src/service.ts";
export default module("my-app", ({ provision }) => {
const db = provision(rawPostgres({ name: "database" }));
provision(api, { deps: { db } });
});
```
```ts
// src/server.ts
import { SQL } from "bun";
import service from "./service.ts";
const { db } = service.load();
const sql = new SQL({ url: db.url, max: 1, idleTimeout: 10 });
```
- `rawPostgres()` comes from the package root and delivers `{ url }`. You own the client and the schema; the deploy runs no migrations for it.
- `postgres(contract)` comes from `@prisma/composer-prisma-cloud/orm`, together with `dataContract`. It delivers `{ url, client }` with a client typed by your Prisma ORM data contract, and the deploy applies the committed `migrations/` before the service starts. Every schema change goes through `prisma contract emit` and `prisma migration plan` first. See [Databases](https://www.prisma.io/docs/composer/databases).
- `postgres` is not exported from the package root in 0.28.0, and `postgres()` with no argument does not declare a plain database. Use `rawPostgres()` for that.
- Each stage gets its own empty databases. Production data is never copied.
Buckets work the same way: `bucket()` from the package root, delivering `{ url, bucket, accessKeyId, secretAccessKey }`.
## Configuration and Secrets
Composer services do not read `process.env`. Declare the service's configuration as an input schema, and bind each field at `provision()` to a literal, `envParam("NAME")`, or `envSecret("NAME")`. The named values live in the stage's platform environment variables (`project env`). A deploy copies a missing name from the deploying shell and fails early, naming the variable, when neither has it. See [Service input](https://www.prisma.io/docs/composer/service-input).
## Porting From `prisma.compute.ts`
| `prisma.compute.ts` field | Composer equivalent |
|---------------------------|---------------------|
| `app.name` / `apps.<key>` | `compute({ name })`; the root module name is the project name |
| `framework`, `build.command` | Your own `build` script, run before `deploy` |
| `entry`, `build.outputDirectory`, `build.entrypoint` | `node({ module, entry })` or `node({ module, dir, entry })` pointing at built output, or `nextjs({ module, appDir })` |
| `httpPort` | Removed. Listen on `service.port()` |
| `env` file | Platform env vars through `project env add --file`, bound with `envParam`/`envSecret`; a database becomes a declared dependency |
| `region` | `prismaCloud({ region })`, used only when the first deploy creates the project |
| `apps` (monorepo) | One `compute()` per deployable app, all provisioned in one `module.ts` |
| `--branch <name>` on deploy | `deploy --stage <name>` |
Then delete `prisma.compute.ts`, the `compute:deploy` script, and the `@prisma/compute-sdk` dev dependency if nothing else imports it. `.prisma/cli/state.json` from the old CLI is no longer used.
## Bad
```ts
// prisma.compute.ts
import { defineComputeConfig } from "@prisma/compute-sdk/config";
export default defineComputeConfig({
app: { framework: "hono", entry: "src/index.ts", httpPort: 8080 },
});
```
No current command reads this file.
## Good
`module.ts`, `src/service.ts`, and the `composer` section of `prisma.config.ts` as shown above, with a build script that writes the file `entry` names.
## References
- [Deploy your first app](https://www.prisma.io/docs/prisma-compute/deploy)
- [Port an existing app](https://www.prisma.io/docs/composer/porting-an-app)
- [Composer getting started](https://www.prisma.io/docs/composer/getting-started)
@@ -0,0 +1,75 @@
# app-framework-builds
Each framework deploys through a build adapter in its `service.ts` that points at the output your own build produced.
## Priority
CRITICAL
## Why It Matters
Neither `prisma deploy` nor the deploy action detects a framework or runs a build. The service declaration names the built output, and a mismatch between it and what the build writes fails at assemble time or at boot. There are no framework keys any more (`--framework nextjs` and similar belonged to the removed CLI).
## Adapters
| Adapter | Import | Use for |
|---------|--------|---------|
| `node({ module: import.meta.url, entry })` | `@prisma/composer/node` | One self-contained ESM file with every dependency inlined except runtime built-ins (`bun`, `bun:*`, `node:*`) |
| `node({ module: import.meta.url, dir, entry })` | `@prisma/composer/node` | A built directory copied as-is; `entry` is a file inside `dir` |
| `nextjs({ module: import.meta.url, appDir })` | `@prisma/composer/nextjs` | A Next.js app built with `output: "standalone"`; add `nextjsBuild()` to the `composer` extensions |
| `dependencies: "external"` on either `node()` form | `@prisma/composer/node` | A build that leaves packages as imports. The deploy traces `entry` and stages the installed packages it imports, which can take minutes. Composer 0.29.0 and later |
Paths are relative to the service module. With the default `dependencies: "bundled"`, the deploy copies only what the build produced and never ships the project's `node_modules`, for a single file and for a `dir` build alike, so a package left as an import fails at boot with `Cannot find package`. Bundle it, or set `dependencies: "external"`. A `dir` tree must contain no symlinks and `entry` must not leave `dir`. Two services in one package need two separate builds, one per entry.
## Per Framework
The shapes `create-prisma` 0.14.1 generates, with `service.ts` at the project root:
| Framework | Build script | `build:` in `service.ts` | Requirements |
|-----------|--------------|--------------------------|--------------|
| Hono, Elysia, NestJS, plain server | Bundle to one file (`bun build src/index.ts --target=bun --outfile dist/server.mjs`, or tsdown bundling every non-built-in dependency) | `node({ module: import.meta.url, entry: "./dist/server.mjs" })` | The entry starts the HTTP server itself |
| Next.js | `next build` | `nextjs({ module: import.meta.url, appDir: "." })` | `output: "standalone"` in `next.config.*`; `nextjsBuild()` in `prisma.config.ts` |
| Nuxt | `nuxt build` | `node({ module: import.meta.url, dir: "./.output", entry: "server/index.mjs" })` | Default Nitro Node server preset |
| TanStack Start | `vite build` | `node({ module: import.meta.url, dir: "./.output", entry: "server/index.mjs" })` | `nitro()` from `nitro/vite` in the Vite plugins |
| Astro | `astro build` | `node({ module: import.meta.url, dir: "./dist", entry: "server/entry.mjs", dependencies: "external" })` | `output: "server"`, `adapter: node({ mode: "standalone" })` from `@astrojs/node`. The adapter leaves packages as imports, so Composer 0.29.0 and later need `dependencies: "external"`; `create-prisma` 0.14.x pins Composer 0.28.0 and omits it |
| SvelteKit | `vite build` | `node({ module: import.meta.url, dir: "./build", entry: "index.js" })` | `@sveltejs/adapter-node`, with `ssr: { noExternal: true }` in `vite.config.ts` so the build inlines packages; without it, add `dependencies: "external"` |
| Turborepo | `turbo run build` | `node({ module: import.meta.url, entry: "./apps/server/dist/server.mjs" })` | One `compute()` per deployable app |
Verify the actual output path after a local build before writing `entry` or `dir`.
## Host and Port
The runtime exports the reserved port as `PORT` and sets `HOST` to `0.0.0.0` when it is unset. Code you write reads `service.port()` and binds `0.0.0.0`. Framework servers that read `PORT` themselves (Next.js standalone, Nitro, Astro's Node adapter, SvelteKit's Node adapter) work without changes. Never set `HOST` or `HOSTNAME` to `localhost` or `127.0.0.1` in deploy env.
```ts
// Elysia
app.listen({ port: service.port(), hostname: "0.0.0.0" });
// NestJS
await app.listen(service.port(), "0.0.0.0");
```
## Next.js Pages That Read the Service
A page, route handler, or server action that calls `service.load()` or `service.input()` needs `export const dynamic = "force-dynamic"`. The runtime environment does not exist at build time, and Next.js does not re-read it for prerendered routes.
## Bad
```ts
build: node({ module: import.meta.url, entry: "./src/index.ts" }),
```
The entry is source, not a self-contained build. Imports from `node_modules` fail at boot.
## Good
```ts
build: node({ module: import.meta.url, entry: "./dist/server.mjs" }),
```
with `"build": "bun build src/index.ts --target=bun --outfile dist/server.mjs"`.
## References
- [Port an existing app](https://www.prisma.io/docs/composer/porting-an-app)
- [Framework guides](https://www.prisma.io/docs/guides)
@@ -0,0 +1,89 @@
# automation-create-prisma
`create-prisma` scaffolds a new Prisma 8 project that is already a Composer app, and can deploy it once.
## Priority
MEDIUM
## Why It Matters
`create-prisma` writes a working Composer app in one command, but only for new projects, and its `--json` mode deploys by default. Older versions generated `prisma.compute.ts` and a `compute:deploy` script; current versions generate `module.ts`, `service.ts`, and a `composer` section instead.
Verified with `create-prisma` 0.14.1.
## Commands
```bash
npx create-prisma@latest --help
```
| Flag | Meaning |
|------|---------|
| `[project-name]`, `--name` | Project directory and root module name |
| `--template` | `minimal`, `hono`, `elysia`, `nest`, `next`, `turborepo`, `svelte`, `astro`, `nuxt`, `tanstack-start` |
| `--provider` | `postgres` or `mongo` |
| `--authoring` | `psl` or `typescript` for the data contract |
| `--package-manager` | `npm`, `pnpm`, `yarn`, `bun`, `deno` |
| `--deploy` | Build and deploy after scaffolding |
| `--workspace` | Workspace id or name to deploy into |
| `--skills` | Agents to install package skills for (`claude,cursor,agents,devin`), or `none` |
| `--yes` | Accept defaults; does not deploy |
| `--json` | One JSON result, non-interactive; deploys unless `--no-deploy` |
When to deploy:
| Invocation | Deploys |
|------------|---------|
| `--deploy` | Yes |
| `--json` without `--no-deploy` | Yes |
| `--yes` without `--deploy` or `--json` | No |
| Interactive | Asks |
| `--package-manager deno` | Never; no Composer files are generated |
For an unattended scaffold with no deploy, pass both:
```bash
npx create-prisma@latest my-api --template hono --provider postgres --yes --json --no-deploy
```
## What It Generates
- `module.ts` provisioning one service named `app`. With `--provider postgres`, it also provisions `postgres({ name: "database", contract, config: "./prisma.config.ts" })` from `@prisma/composer-prisma-cloud/orm`, so the deploy applies the generated migrations. With `--provider mongo`, it binds `envSecret("MONGODB_URL")` to the service input.
- `service.ts` with the build adapter for the template; see [`app-framework-builds.md`](app-framework-builds.md).
- `prisma.config.ts` with `orm`, `skills`, and `composer` sections. The `composer` section uses `prismaCloud({ region: "us-east-1" })`; change the region before the first deploy if the app belongs elsewhere.
- Scripts: `composer:dev` (`prisma dev module.ts`), `composer:deploy` (`prisma deploy module.ts`), `dev:composer` (build, then `composer:dev`), and `deploy` (build, then `composer:deploy`). The `deploy` script targets production; pass a stage with `npx prisma deploy module.ts --stage <name>` instead.
- `/.prisma-composer` in `.gitignore`.
## Deploy Flow
With deploy enabled, `create-prisma` runs `prisma auth whoami`, selects the workspace, checks that no project already has the app's name, runs the build script, and runs `prisma deploy module.ts --report <tmp>`. Without a session it signs in interactively in a terminal; in `--json` mode or without a TTY it stops with `not_authenticated`, keeps the scaffold, and tells the user to sign in and run the `deploy` script.
## Rules
- Do not run `create-prisma` inside an existing app to deploy it. Port the app ([`app-composer-setup.md`](app-composer-setup.md)) and use `prisma deploy`.
- Use the scripts the scaffold actually wrote; read `package.json` rather than reconstructing them.
- A scaffold's first deploy is production. Ask the user before using `--deploy` or `--json` without `--no-deploy`.
## Bad
```bash
npx create-prisma@latest my-api --template hono --yes --json
```
This deploys to production without anyone deciding to.
## Good
```bash
npx create-prisma@latest my-api --template hono --provider postgres --yes --json --no-deploy
cd my-api
npm run dev:composer
```
Verify locally, then deploy a stage with `npm run build && npx prisma deploy module.ts --stage preview`.
## References
- [create-prisma on npm](https://www.npmjs.com/package/create-prisma)
- [Deploy your first app](https://www.prisma.io/docs/prisma-compute/deploy)
@@ -0,0 +1,99 @@
# automation-rest-api
Programmatic access to Compute: the Management REST API, the Composer control API, and what became of `@prisma/compute-sdk`.
## Priority
MEDIUM
## Why It Matters
Deploys now go through Composer, so low-level deploy clients from the earlier flow create resources that no `module.ts` declares. Use the CLI or the control API to deploy, and the REST API to read state, stream logs, and integrate with other tools.
## Choose the Surface
| Goal | Use |
|------|-----|
| Deploy or tear down an app | `prisma deploy`, or `deploy`/`destroy` from `@prisma/composer/control` |
| Deploy from GitHub | `prisma/cloud-deploy-action` ([`deploy-on-push.md`](deploy-on-push.md)) |
| Read services, versions, builds, logs from your own tool | REST API |
| A coding agent with its own credential and approval policy | [Agent enrollment](https://www.prisma.io/docs/prisma-compute/agent-enrollment) (REST API) |
## REST API
Base URL `https://api.prisma.io/v1`. The current OpenAPI document is at `https://api.prisma.io/v1/doc`; read it for exact payloads instead of copying shapes from memory. Authenticate with `Authorization: Bearer <workspace service token>` or an OAuth token. The Compute endpoints are marked experimental and can change.
| Resource | Paths |
|----------|-------|
| Projects | `GET/POST /v1/projects`, `GET/PATCH/DELETE /v1/projects/{id}` |
| Branches | `GET/POST /v1/projects/{projectId}/branches` (`gitName` filter), `GET/PATCH/DELETE /v1/branches/{branchId}` |
| Services | `GET/POST /v1/services` (filter by `projectId`, `branchId`, `branchGitName`), `GET/PATCH/DELETE /v1/services/{serviceId}` |
| Versions (deployments) | `GET/POST /v1/services/{serviceId}/deployments`, `GET/DELETE /v1/deployments/{deploymentId}`, `POST /v1/deployments/{deploymentId}/start`, `POST /v1/deployments/{deploymentId}/stop` |
| Promote and rollback | `POST /v1/services/{serviceId}/promote`, `POST /v1/services/{serviceId}/rollback` |
| Logs | `GET /v1/deployments/{deploymentId}/logs` |
| Builds | `GET/POST /v1/builds` (filter by `projectId`, `branchId`, `state`), `GET/PATCH /v1/builds/{buildId}` |
| Domains | `GET/POST /v1/services/{serviceId}/domains`, `GET/DELETE /v1/domains/{domainId}`, `POST /v1/domains/{domainId}/retry` |
| Environment variables | `GET/POST /v1/environment-variables` (filter by `projectId`, `class`, `key`, `branchId`), `GET/PATCH/DELETE /v1/environment-variables/{envVarId}` |
The `/v1/apps` paths (`/v1/apps`, `/v1/apps/{appId}/deployments`, `/promote`, `/rollback`, `/domains`) are deprecated. Use the `/v1/services` equivalents.
### Logs
`GET /v1/deployments/{deploymentId}/logs` takes `tail`, `from_start`, or `cursor`. A plain GET returns newline-delimited JSON: `type: "log"` records, then a `type: "terminal"` record carrying the cursor for the next page. Send `Upgrade: websocket` to follow live; a live segment ends after 10 minutes with the same terminal record, so reconnect with the cursor.
### Builds
A build record is what a deploy reports about itself: the deploy action and `prisma deploy` both write to it. `GET /v1/builds/{buildId}` returns its `state`, `failingStep`, `errorMessage`, `externalLogUrl` (for example the GitHub Actions run), and `deployedUrl`. There is no build log endpoint; the log lives at `externalLogUrl` or in the terminal that ran the deploy. `prisma deploy --build-id <id>` joins a build record your CI created with `POST /v1/builds`.
### Secrets
Environment variable values are write-only: no endpoint returns them. Do not log tokens, connection URLs, or env values.
## Control API
`@prisma/composer/control` runs the CLI's operations in-process and returns `{ ok: true, value }` or `{ ok: false, failure }` with a dotted `failure.code`:
```ts
import { deploy } from "@prisma/composer/control";
import prismaConfig from "./prisma.config.ts";
const result = await deploy({
entry: "module.ts",
config: { value: prismaConfig.composer, file: "prisma.config.ts" },
stage: "pr-42",
});
if (!result.ok) throw new Error(`${result.failure.code}: ${result.failure.message}`);
```
- Operations: `deploy`, `destroy`, `dev`, `log`. `config` is required in 0.28.0.
- `deploy` and `destroy` read only `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID`, never the `auth login` session. Without them they fail with `environment variable PRISMA_WORKSPACE_ID is required`.
- `log` tails a locally running `prisma dev` app; it is not a platform log reader.
- The engine's own output still streams to stdio.
## `@prisma/compute-sdk`
`@prisma/compute-sdk` 0.43.0 is still on npm, and the CLI uses it internally for service operations and log streaming. Its `deploy()` build strategies and `defineComputeConfig` belong to the removed flow: no CLI command reads `prisma.compute.ts`, and its app client calls the deprecated `/v1/apps` endpoints. Do not start new deploy automation on it, and remove it from projects that used it only for `defineComputeConfig`.
## Bad
```ts
import { ComputeClient, PreBuilt } from "@prisma/compute-sdk";
await new ComputeClient(apiClient).deploy({
strategy: new PreBuilt({ appPath: "./dist", entrypoint: "index.js" }),
projectId: "proj_abc",
appName: "my-app",
});
```
This creates an app outside any Composer declaration, through deprecated endpoints.
## Good
Deploy with `prisma deploy module.ts` or the control API, and read the result back with `GET /v1/services?projectId=...` and `GET /v1/deployments/{deploymentId}/logs`.
## References
- [REST API](https://www.prisma.io/docs/rest-api)
- [OpenAPI document](https://api.prisma.io/v1/doc)
- [`deploy` command and control API](https://www.prisma.io/docs/cli/deploy)
@@ -0,0 +1,137 @@
# cli-command-map
The Prisma 8 CLI commands an agent uses to deploy and operate a Compute app, with the flags, output, and targeting rules that matter.
## Priority
CRITICAL
## Why It Matters
Agents trained on the earlier Compute CLI reach for `@prisma/cli app deploy`, `build logs`, and `database ...`, which no longer exist. The current CLI also refuses to guess: it needs explicit targets, exact consent tokens for destructive actions, and the Prisma 8 binary.
Verified with `prisma` 8.0.0-rc.21 and `@prisma/cli` 8.0.0-rc.21 (same commands; the `@prisma/cli` binary is named `prisma-cli`).
## Which Binary Runs
| Situation | Run |
|-----------|-----|
| Composer app with `prisma` 8.x in `devDependencies` | `npx prisma <command>` (or `bunx prisma`, `pnpm exec prisma`) |
| No local `prisma`, or a Prisma 6/7 project | `npx @prisma/cli@latest <command>` |
A Prisma 6 or 7 `prisma` binary has no `auth`, `project`, `service`, `branch`, `git`, or `deploy` commands. In Prisma 7, `dev` starts a local Prisma Postgres server and `postgres` only links an existing database. `npx prisma --version` prints the version; in a non-terminal it prints a JSON envelope with `result.version`.
## Commands
| Command | Subcommands and flags | Notes |
|---------|-----------------------|-------|
| `auth` | `login`, `logout`, `whoami`, `workspace list`, `workspace use [id-or-name]`, `workspace logout <id-or-name>` | `login` opens a browser. `logout` ends every stored session |
| `project` | `list`, `show [id-or-name]`, `create <name> [--region]`, `link [id-or-name]`, `rename <name>`, `delete <id-or-name>`, `transfer <id-or-name>` | `create` and `link` write `.prisma/local.json` and gitignore it |
| `project env` | `add [KEY=VALUE\|KEY]`, `update`, `list`, `delete <key>`; scope with `--role production\|preview` or `--branch <git-name>`; `--file <dotenv>` for `add`/`update` | Values are write-only |
| `postgres` | `list`, `show`, `create <name> [--region] [--branch]`, `usage`, `delete`, `backup list\|restore`, `connection list\|create\|rotate\|delete` | Connection URLs print once |
| `bucket` | `list`, `create [--name] [--branch]`, `delete <bucket-id>`, `key list\|create\|delete` | Keys print once |
| `branch` | `list` | No create or delete |
| `git` | `connect [git-url]`, `disconnect` | `connect` needs an interactive terminal |
| `service` | `list`, `create <name> [--region] [--branch]`, `show <service>`, `open <service>`, `logs <service>`, `delete <service>` | `--project`, `--branch` on each |
| `service version` | `list <service>`, `show <id>`, `promote <id>`, `rollback <service> [--to <id>]`, `start <id>`, `stop <id>`, `delete <id>` | Version ids look like `cpv_...` |
| `service domain` | `add`, `show`, `wait [--timeout]`, `retry`, `delete`, each `<hostname> --service <service>` | Production branch only |
| `deploy <entry>` | `--name`, `--stage`, `--report <path>`, `--build-id <id>` | Composer app; does not build |
| `dev <entry>` | `--name`, `--fresh` | Composer app, local, no credentials |
| `init` | `--skills <agents>`, `--postinstall/--no-postinstall` | Local only; never edits an existing `prisma.config.ts` |
| `skills` | `sync [--disable\|--enable]`, `list` | Copies package-shipped skills |
| `feedback <message>` | `--email <address>` | Anonymous unless `--email` |
`contract`, `db`, `migration`, `orm`, `lsp`, and `telemetry` belong to Prisma ORM and the CLI itself; they are outside this skill. There is no `destroy`, `log`, `app`, `build`, `database`, `agent`, or `compute` command.
## Global Flags
Every command accepts these:
- `--json` (same as `--format json`), or `--format human|json|markdown`
- `--interactive` / `--no-interactive`
- `--yes`: accept prompt defaults; it cannot grant consent
- `--confirm <token>`: grant one consent prompt; repeat for several
- `--config <path>`: read another config file instead of `./prisma.config.ts`
- `--log-level error|warn|info|verbose`, `-v`, `-q`, `--color/--no-color`
When stdout is not a terminal the CLI already writes JSON; pass `--json` anyway so the intent is explicit. When `CI` is set (and not `false`), prompts are off.
## JSON Output
Each command prints one envelope:
```json
{"kind":"result","envelope":{"ok":false,"commandId":"","error":{"code":"CLI.UNKNOWN_COMMAND","summary":"No command registered for `app`","nextActions":[{"kind":"run-command","command":"prisma --help"}]},"nextActions":[]}}
```
On success, `envelope.ok` is `true` and `envelope.result` holds the data. On failure, branch on `envelope.error.code`; codes are a stable contract and messages are not. Codes you will meet:
| Code | Meaning |
|------|---------|
| `CLI.UNKNOWN_COMMAND` | The command does not exist in this CLI version |
| `CLI.INVALID_ARGUMENTS` | Unknown flag or bad argument |
| `CLI.CONSENT_REQUIRED` | A destructive action needs `--confirm <token>` |
| `CLI.INTERACTION_REQUIRED` | The command needs an interactive terminal (`git connect`, `auth login`) |
| `CLI.CREDENTIALS_REQUIRED` | No stored session and no `PRISMA_SERVICE_TOKEN` |
| `PROJECT.SETUP_REQUIRED` | No `--project` and no `.prisma/local.json` link |
| `SERVICE.TARGET_REQUIRED` | The service argument is missing |
| `SERVICE.NO_PREVIOUS_VERSION` | `rollback` without `--to` found nothing to roll back to |
| `CLI.CONFIG_UNREADABLE` | `prisma.config.ts` failed to evaluate (often an `effect` version conflict) |
`deploy --json` also emits step events and ends with `envelope.result.summary.nodes[].entities[]`, where each entity has `kind` (`compute-service`, `postgres-database`), `id`, and, for services, `url`.
## Targeting
- **Project:** `--project <id-or-name>`, else the `.prisma/local.json` link, else `PROJECT.SETUP_REQUIRED`. Only `project create` and `project link` write the link; `deploy` does not.
- **Branch:** `--branch <git-name>`. Without it, commands target the project's default branch, which is production. The current Git branch is not consulted.
- **Service:** the first positional argument. There is no picker and no environment fallback.
- **Version:** version ids are globally unique, so `service version show|promote|start|stop|delete <id>` need no other scope.
Environment variables the CLI reads for the Platform: `PRISMA_SERVICE_TOKEN` (overrides stored sessions; a set-but-empty value is an error), `PRISMA_WORKSPACE_ID` (the workspace for a service token), `PRISMA_REGION` (region for a project the first deploy creates), and `PRISMA_COMPOSER_REPORT_FILE` (same as `--report`). `PRISMA_SKILLS_CHECK=0` silences the out-of-date skills notice. It does not read `PRISMA_PROJECT_ID`, `PRISMA_APP_ID`, or `PRISMA_SERVICE_ID`.
## Consent Tokens
These commands stop at a consent prompt. In a non-interactive run, pass the token with `--confirm`; `--yes` fails with `CLI.CONSENT_REQUIRED`.
| Command | Token |
|---------|-------|
| `project delete <project>` | Project id |
| `project transfer <project>` | Project id |
| `postgres delete <database>` | Database id |
| `postgres backup restore <database> --backup <id>` | Target database id |
| `postgres connection rotate <connection-id>` | Connection id |
| `postgres connection delete <connection-id>` | Connection id |
| `bucket delete <bucket-id>` | Bucket id |
| `service delete <service>` | Service name |
| `service version delete <version-id>` | Version id |
| `service version rollback <service> --to <version-id>` | Target version id |
| `service domain delete <hostname>` | Hostname |
```bash
npx prisma service version rollback api --project my-app --to cpv_123 --confirm cpv_123 --json
```
Run a consent-gated command only when the user asked for that specific action.
## Bad
```bash
bunx @prisma/cli@latest app deploy --prod --yes --env .env
npx prisma service delete api --yes
```
`app deploy` fails with `CLI.UNKNOWN_COMMAND`, and `--yes` cannot answer the consent prompt for `service delete`.
## Good
```bash
npm run build
npx prisma deploy module.ts --stage feat-search --json
npx prisma service delete api --branch feat-search --confirm api --json
```
## References
- [CLI reference](https://www.prisma.io/docs/cli)
- [Global flags](https://www.prisma.io/docs/cli/global-flags)
- [Error reference](https://www.prisma.io/docs/cli/error-reference)
@@ -0,0 +1,121 @@
# debug-deploy-failures
Diagnose Compute failures from the error code first, then the symptom.
## Priority
HIGH
## Why It Matters
Most failures carry a structured code and a `nextActions` list that names the fix. Guessing from the message, or retrying commands from the removed CLI, wastes the user's time and can deploy into the wrong place.
## First Checks
```bash
npx prisma --version
npx prisma auth whoami --json
npx prisma project show --json
ls module.ts prisma.config.ts
node --version
test -f .env && sed 's/=.*/=<redacted>/' .env
```
Re-run the failing command with `--json` (and `--report deploy-report.json` for `deploy`) and read `error.code`, `error.why`, and `nextActions`. Never print token or connection-string values.
## CLI and Setup
| Symptom | Cause | Fix |
|---------|-------|-----|
| `CLI.UNKNOWN_COMMAND` for `app`, `build`, `database`, `agent`, `destroy`, `compute` | The command was removed | Use the replacement in the SKILL.md table |
| `Unknown command "auth"` or `"deploy"`, or `prisma dev` starts a database server | A Prisma 6 or 7 `prisma` binary | Install `prisma` 8.x as a dev dependency, or use `npx @prisma/cli@latest` |
| `ERR_UNKNOWN_FILE_EXTENSION` | Node.js too old to load `module.ts`. Composer supports 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer | Upgrade Node.js |
| `CLI.CONFIG_UNREADABLE` with a module error from `effect` | Another `effect` version hoisted over the one `@prisma/composer` pins | Match the app's `effect` to `@prisma/composer`'s `dependencies.effect`, or force it with `overrides` (npm), `pnpm.overrides` or `pnpm-workspace.yaml` `overrides` (pnpm), `resolutions` (Yarn); reinstall |
| `CLI.CONFIG_SECTION_INVALID` with `CONFIG.SECTION_MISSING` | No `composer` section in `prisma.config.ts` | Add the section ([`app-composer-setup.md`](app-composer-setup.md)) |
| `CONFIG.FILE_RETIRED` or `CONFIG.FIELD_RETIRED` | A `prisma-composer.config.ts` or `configPath` from an older Composer | Move `extensions` and `state` into the `composer` section and delete the old file |
| `ASSEMBLE.EXTENSION_MISSING` | A service uses `nextjs()` but `nextjsBuild()` is not in `extensions` | Add `nextjsBuild()` from `@prisma/composer/nextjs/control` |
| `COMPOSE.GRAPH_INVALID` naming an id | A provision id or service name with a hyphen or other non-alphanumeric character | Use `[A-Za-z0-9]` only; at least three characters for provision ids |
| Bun APIs fail while the CLI loads `module.ts` | The `prisma` bin runs under Node | `bun node_modules/.bin/prisma deploy module.ts` |
## Auth and Project
| Symptom | Cause | Fix |
|---------|-------|-----|
| `CLI.CREDENTIALS_REQUIRED` | No session and no service token | Ask the user to run `npx prisma auth login`; in CI set `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID` |
| Error about a blank `PRISMA_SERVICE_TOKEN` | The variable is set but empty | Unset it or provide a token |
| Commands act in the wrong workspace | Another stored session is current, or a service token overrides it | `auth workspace list --json`, then `auth workspace use <id>`; unset `PRISMA_SERVICE_TOKEN` to use sessions |
| `PROJECT.SETUP_REQUIRED` after a successful deploy | `deploy` does not link the directory | `npx prisma project link <app-name>` or pass `--project` |
| `project "<name>" does not exist yet and no deploy region is configured` | First deploy without a region | `prismaCloud({ region })` or `PRISMA_REGION` |
| Deploy refuses an existing project name (`HostedStateBootstrapError`, or `a project with this name already exists`) | A project with the module's name exists that this module did not create | `deploy --name <unique-name>`, or rename the module |
| Deploy fails immediately naming another holder | Another deploy of the same environment holds the state lease | Wait for it; a crashed deploy's lease expires after about a minute |
## Deploy Pipeline
| Symptom | Cause | Fix |
|---------|-------|-----|
| `ASSEMBLE.BUILD_FAILED` | The built entry or directory is missing or invalid (no build, wrong path, symlinks in a `dir` build) | Run the build and check that `entry`/`dir` match its output, relative to `service.ts` |
| `ASSEMBLE.SERVICE_MISSING` | The module provisions no service | Provision at least one `compute()` service |
| `DEPLOY.STAGE_INVALID` | The stage is not a valid Git ref name | Rename the stage |
| A connection input "declares param ..., but its producer ... did not supply it" | An authored connection or extension does not supply a declared value | Fix the producer's outputs; mark the param optional only if absent is valid |
| Deploy fails early naming a missing variable | An `envParam`/`envSecret` name is unset on the stage and in the shell | Export it in the deploying shell or set it with `project env add` |
| `MIGRATION_PATH_NOT_FOUND` | A `postgres(contract)` schema change has no committed migration | `prisma contract emit`, `prisma migration plan --name <slug>`, commit `migrations/` |
| `DEPLOY.ENGINE_FAILED` | The deploy engine or the platform rejected the generated stack | Read the engine lines in the error; it names `.prisma-composer/alchemy.run.ts` and a command that reproduces the failure |
| `DEPLOY.BUILD_REQUIRED` from `destroy` | `destroy` packages the built app too | Build, then destroy |
## Deployed but Not Working
| Symptom | Cause | Fix |
|---------|-------|-----|
| Public URL times out or returns 502 at once | Listener on `localhost`, wrong port, or a crash at boot | Bind `0.0.0.0` on `service.port()`; read `npx prisma service logs <service>` |
| `Cannot find package`, `Cannot find module`, or `ENOENT while resolving package` in logs | The build left a package as an import, and the default `dependencies: "bundled"` ships only the build output | Bundle every dependency (`bun build --target=bun`), or set `dependencies: "external"` on `node()` so the deploy stages the installed packages |
| 502s in a loop after the app was idle | A database client dies when idle connections close | Small reconnect-friendly pool; log `uncaughtException` and `unhandledRejection` instead of exiting |
| `504 Gateway Time-out` with an `openresty` body after about 60 seconds | The handler sent nothing for 60 seconds | Respond first, continue under `waitUntil`, or queue the work |
| Background work stops or never finishes | The instance slept | `waitUntil` or `KeepAwakeGuard` from `@prisma/compute`; persist progress |
| `401` from a deployed `/rpc/<method>` | Composer's service-key check; only wired consumers may call | Test through a consumer or locally; not a deploy failure |
| `RUNTIME.TEMPORAL_UNAVAILABLE` or a failure on the first timestamp read | No global `Temporal` on the runtime | Import a Temporal polyfill at the server entry, or use string column types |
| Next.js page shows build-time or missing values from `service.load()` | The route was prerendered | `export const dynamic = "force-dynamic"` |
| `ECONNRESET` calling another service | The callee was cold-starting | Retry |
| A changed env var has no effect | Values resolve at deploy time | Redeploy (or promote) |
| A resource disappeared after a deploy | Its `provision()` was removed from `module.ts` | Restore the declaration and redeploy |
| A preview's database is empty | Stages get their own empty databases | Seed it; production data is never copied |
## Deploy on Push
| Symptom | Cause | Fix |
|---------|-------|-----|
| Push builds nothing, no error | No workflow on the pushed branch | Add `.github/workflows/prisma-deploy.yml` ([`deploy-on-push.md`](deploy-on-push.md)) |
| Green run with `skipped-no-credential` | Repository not connected, no `id-token: write`, or a fork | `npx prisma git connect`, add the permission |
| `CLI.UNKNOWN_COMMAND` inside the action | Action release v1.5.0 or older | Use `prisma/cloud-deploy-action@v1` (v1.9.0 or later) |
| Install step fails on pnpm or Yarn | The action auto-detects npm and Bun only | Set `install-command` |
## Custom Domains
`npx prisma service domain show <hostname> --service <service> --json` prints the state and the DNS record it expects. `SERVICE.DOMAIN_DNS_NOT_CONFIGURED` means the CNAME is not visible yet; create it, then run `add` again. After fixing DNS on a `failed` domain, run `service domain retry`. `SERVICE.BRANCH_NOT_DEPLOYABLE` means a preview branch was targeted; domains are production only.
## Local Development
- `dev` lists services from other apps: the emulators are shared machine-wide daemons. Ignore names not in your `module.ts`.
- `dev --fresh` fails for an app with a database in this release. Follow the reset steps in [Local development](https://www.prisma.io/docs/composer/local-development#whats-local-vs-whats-real).
- Windows is not supported for the local stack.
## Report CLI Bugs
When a failure survives these checks or the CLI reports an internal error, send it to the CLI team without secrets:
```bash
npx prisma feedback "deploy failed with DEPLOY.UNEXPECTED: <first error line>"
```
## Bad
Retrying `bunx @prisma/cli@latest app deploy --prod --yes` after `CLI.UNKNOWN_COMMAND`, or editing code before reading `error.code`.
## Good
Re-run with `--json --report deploy-report.json`, read `error.code` or `failure.code`, apply the fix this table names, rebuild, and deploy again.
## References
- [Deploying](https://www.prisma.io/docs/composer/deploying)
- [Request timeout](https://www.prisma.io/docs/compute/request-timeout)
- [CLI error reference](https://www.prisma.io/docs/cli/error-reference)
@@ -0,0 +1,138 @@
# deploy-cli-and-ci
Deploy a Composer app with `prisma deploy`: production or a stage, from a workstation or from CI.
## Priority
HIGH
## Why It Matters
`deploy` does not build, chooses the project by the app's name rather than the directory link, and targets production unless told otherwise. Getting any of these wrong ships stale code, deploys into the wrong project, or changes production by accident.
## Commands
```bash
npx prisma deploy module.ts --help
npx prisma dev module.ts --help
```
| You want to | Run |
|-------------|-----|
| Run the app locally, no credentials | `npm run build && npx prisma dev module.ts` |
| Deploy a preview | `npm run build && npx prisma deploy module.ts --stage <name>` |
| Deploy production | `npm run build && npx prisma deploy module.ts` |
| Deploy under another app name | `npx prisma deploy module.ts --name <name>` |
| Write the outcome as JSON | `npx prisma deploy module.ts --report deploy-report.json` |
`<entry>` is the file whose default export is the root module, usually `module.ts`.
## Before Deploying
1. Build. `deploy` and `dev` package what the build already produced.
2. Run `npx prisma dev module.ts` and request each local URL it prints. It runs the same pipeline against local emulators, with a real local Postgres, and needs no account. `Ctrl-C` leaves local data in place.
3. Check auth: `npx prisma auth whoami --json`. If no one is signed in, ask the user to run `npx prisma auth login`; it opens a browser and cannot be done by an agent.
4. For a new project, set a region: `prismaCloud({ region })` in `prisma.config.ts` or `PRISMA_REGION`.
5. Decide the target with the user. No `--stage` changes production.
## Production and Stages
- No `--stage`: production. In platform terms the app is a project named after the root module, and production is its default branch.
- `--stage <name>`: a complete, isolated copy of the app as a preview branch of the same project, with its own services, databases, and configuration. Only the code is shared. The name must be a valid Git ref name; an invalid one fails the deploy.
- Redeploying is idempotent: the deploy compares the module with the recorded deploy state and applies the difference. Removing a `provision()` from the module deletes that resource on the next deploy. A deploy never deletes the last service, the project, or a branch.
- Deploy state lives on the platform, per branch, so a workstation and CI see the same state. Concurrent deploys of one environment take turns through a lease; a second deploy fails while the first holds it, and a crashed deploy's lease expires after about a minute.
## Which Project
The deploy finds the project in the workspace by the root module name (or `--name`) and creates it if none exists. It does not read or write `.prisma/local.json`, and it has no `--project` flag.
- A project with the same name that this module did not create can stop the deploy. Deploy with `--name <unique-name>` or rename the module.
- After the first deploy, link the directory so other commands target the project: `npx prisma project link <app-name>`. Otherwise pass `--project <app-name>` to `service`, `git`, and `project env` commands.
## Credentials
| Where | How |
|-------|-----|
| Workstation | `npx prisma auth login` once; later commands, including an agent's, reuse the stored session |
| CI or headless | `PRISMA_SERVICE_TOKEN` (a workspace service token from Console settings) and `PRISMA_WORKSPACE_ID` |
| GitHub Actions on a connected repository | OIDC through `prisma/cloud-deploy-action`; no stored secret. See [`deploy-on-push.md`](deploy-on-push.md) |
A non-empty `PRISMA_SERVICE_TOKEN` takes priority over stored sessions. A set-but-empty value is an error. Never print the token; check presence with `test -n "${PRISMA_SERVICE_TOKEN:-}"`.
Input fields bound with `envParam`/`envSecret` are stored per stage. A fresh stage starts with none, so CI must export those values; the first deploy copies missing names from the shell to the stage.
## Output
A deploy prints each step and ends with the app tree: every authored name, the platform resource it became, and public URLs.
```text
my-app
├─ db postgres-database db_def456
└─ api compute-service cps_abc123
https://xyz.ewr.prisma.build
```
With `--json`, the final envelope's `result.summary.nodes[].entities[]` lists what was deployed, each with `kind`, `id`, and `url` for services. `--report <path>` (or `PRISMA_COMPOSER_REPORT_FILE`) writes the outcome as JSON, including `failure.code` when the deploy fails. Request each service URL after the deploy:
```bash
curl -i https://xyz.ewr.prisma.build
```
## CI Without GitHub
Any CI runs the same commands with the two credential variables as secrets:
```bash
npm ci
npm run build
npx prisma deploy module.ts --stage "pr-$PR_NUMBER" --json --report deploy-report.json
```
`--build-id <id>` joins a build record your CI already created through `POST /v1/builds`.
## Teardown
The CLI has no `destroy` command, and `branch` has no delete.
- Connected GitHub repository: deleting the Git branch tears down the matching preview (services, databases, buckets). Production and the default branch are never torn down this way.
- Otherwise: call `destroy` from `@prisma/composer/control` with an explicit target. It needs `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID` in the environment and the app already built:
```ts
// destroy-stage.ts
import { destroy } from "@prisma/composer/control";
import prismaConfig from "./prisma.config.ts";
const result = await destroy({
entry: "module.ts",
config: { value: prismaConfig.composer, file: "prisma.config.ts" },
target: { kind: "stage", stage: "pr-42" },
});
if (!result.ok) throw new Error(`${result.failure.code}: ${result.failure.message}`);
```
`config` is required in `@prisma/composer` 0.28.0: the `composer` section and the path of the file that declares it. Pass `name` when the app was deployed with `--name`. `target` is `{ kind: "stage", stage }` or `{ kind: "production" }` and has no default. Destroying a stage removes its resources, branch, and data; destroying production removes its resources and deletes the project once it is empty. Get the user's confirmation first. The deploy action's `mode: destroy` does not work against the current CLI.
## Bad
```bash
npx prisma deploy module.ts
```
Run without a build, this deploys whatever stale output is on disk, and without `--stage` it targets production.
## Good
```bash
npm run build
npx prisma dev module.ts # verify locally, then Ctrl-C
npx prisma auth whoami --json
npx prisma deploy module.ts --stage feat-search --json --report deploy-report.json
npx prisma project link my-app
npx prisma service list --branch feat-search --json
```
## References
- [`deploy` command](https://www.prisma.io/docs/cli/deploy)
- [Deploying](https://www.prisma.io/docs/composer/deploying)
- [Local development](https://www.prisma.io/docs/composer/local-development)
@@ -0,0 +1,101 @@
# deploy-on-push
Deploy every pushed branch from the repository's own GitHub Actions with `prisma/cloud-deploy-action`.
## Priority
HIGH
## Why It Matters
The platform does not build on push. A connected repository without a workflow deploys nothing and reports no error, and a workflow without the connection skips with a green run. Both pieces are required, and a push to the default branch deploys production without any further prompt.
## How It Works
1. **The connection.** `prisma git connect` installs the Prisma GitHub App (if needed) and links the repository to the project. It lets workflow runs exchange their GitHub OIDC token for a 30-minute workspace token, and it tears down a preview when its Git branch is deleted.
2. **The workflow.** `prisma/cloud-deploy-action` runs install, your build command, and `prisma deploy <module>`. The default branch deploys production; any other branch deploys a stage named after it.
The Console can do both at once: importing the repository there opens a pull request that adds the workflow. The CLI only creates the connection.
## Setup From the CLI
```bash
npx prisma project link <app-name> # deploy does not link the directory
npx prisma git connect # uses the origin remote; needs an interactive terminal
```
`git connect` opens a browser for the GitHub App install and waits, so a person has to run it. With `--no-interactive` it fails with `CLI.INTERACTION_REQUIRED`; use the Console instead in that case. It fails with `PROJECT.SETUP_REQUIRED` when the directory is not linked.
Then commit the workflow:
```yaml
# .github/workflows/prisma-deploy.yml
name: prisma-deploy
on:
push:
concurrency:
group: prisma-deploy-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
id-token: write
jobs:
deploy:
if: github.ref_type == 'branch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: oven-sh/setup-bun@v2
- uses: prisma/cloud-deploy-action@v1
with:
build-command: npm run build
```
- `id-token: write` is required for the OIDC exchange.
- Bun 1.3.10 or newer must be on the runner `PATH`; `oven-sh/setup-bun@v2` provides it.
- GitHub reads the workflow from the pushed commit, so a branch created before the file existed deploys nothing until it contains the file.
## Action Inputs and Outputs
Inputs (from `action.yml` in v1.9.0):
| Input | Default | Notes |
|-------|---------|-------|
| `build-command` | `npm run build` | Run verbatim; `none` skips the build |
| `install-command` | auto | `npm ci` or `bun install --frozen-lockfile` from the lockfile; pnpm and yarn need an explicit value |
| `module` | `module.ts` | The Composer entry |
| `stage` | derived | Empty: default branch is production, other branches become a stage of the same name |
| `working-directory` | `.` | For monorepos |
| `prisma-version` | `8.0.0-rc.9` | Used only when the repository has no `prisma` dev dependency; otherwise the installed version deploys |
| `mode` | `deploy` | `destroy` does not work against the current CLI |
Outputs: `outcome` (`succeeded`, `failed`, `skipped-no-credential`), `url` (root service), `urls` (JSON map of service address to URL), `build-id`, `report-path`. Repositories that are not connected can pass `PRISMA_SERVICE_TOKEN` from a secret in the step's `env`; an explicit token wins over OIDC.
## Checking a Push
- Watch the run in the repository's Actions tab. The job summary lists each service URL and, on failure, the cause.
- `outcome: skipped-no-credential` means the repository is not connected, `id-token: write` is missing, or the run came from a fork.
- `CLI.UNKNOWN_COMMAND` in the deploy step means an action release up to v1.5.0, which called the removed `prisma composer` commands. Use `@v1` (v1.9.0 or later).
- From a terminal: `npx prisma service list --branch <git-branch> --json`.
- Deleting the Git branch removes the preview's services, databases, and buckets. Production and the default branch are never torn down.
## Bad
Running `npx prisma git connect`, pushing, and reporting the app as deployed. The connection alone builds and deploys nothing.
## Good
Connect, commit `.github/workflows/prisma-deploy.yml`, push a non-default branch first, confirm the run's `url` responds, and only then merge to the default branch with the user's go-ahead.
## References
- [Deploy on push](https://www.prisma.io/docs/compute/deploy-on-push)
- [GitHub integration](https://www.prisma.io/docs/compute/github)
- [prisma/cloud-deploy-action](https://github.com/prisma/cloud-deploy-action)
@@ -0,0 +1,148 @@
# ops-services-and-config
Inspect and operate what a deploy created: services, versions, logs, domains, environment variables, databases, and buckets.
## Priority
HIGH
## Why It Matters
Operations commands target explicitly: the service is a positional argument, the project comes from `--project` or the directory link, and the branch defaults to production. An agent that relies on the old `app` commands, on `PRISMA_SERVICE_ID`, or on the current Git branch reads or changes the wrong thing.
Every example below accepts `--project <id-or-name>` and `--json`. Without `--branch`, commands act on the production branch.
## Services
A service is one HTTP app on a branch. Every deploy creates an immutable version; one version serves traffic at a time.
```bash
npx prisma service list --json
npx prisma service list --branch feat-search --json
npx prisma service show api --json
npx prisma service open api
```
`service show` returns the live version and the service URL. `service create` exists but is rarely needed; deploys create services.
## Logs
```bash
npx prisma service logs api --tail 200
npx prisma service logs api --follow
npx prisma service logs api --branch feat-search
npx prisma service logs api --version-id cpv_123 --from-start
```
Logs default to the live version. Use `--version-id` for a failed or stopped version. `SERVICE.LOGS_FAILED` carries the API status; `SERVICE.LOGS_INCOMPLETE` means the page ended early, so rerun. Build output is not here: read the GitHub Actions run, the terminal that ran `deploy`, the `--report` file, or the Console.
## Versions, Promote, Rollback
```bash
npx prisma service version list api --json
npx prisma service version show cpv_123 --json
npx prisma service version promote cpv_123
npx prisma service version rollback api --to cpv_122 --confirm cpv_122
npx prisma service version stop cpv_123
npx prisma service version start cpv_123
npx prisma service version delete cpv_123 --confirm cpv_123
```
- `promote` takes a version from a preview branch to production by rebuilding it with production env vars.
- `rollback` puts production back on an existing version without a rebuild. Without `--to` it picks the version before the live one (`SERVICE.NO_PREVIOUS_VERSION` or `SERVICE.LIVE_VERSION_UNKNOWN` when it cannot), and it asks for consent with the target version id. In a non-interactive run, list versions first and pass `--to <id> --confirm <id>`.
- A later production deploy creates a new version and makes it live, so fix the code before the next deploy after a rollback.
## Custom Domains
Domains attach to a service's production branch only, CNAME-based, up to three per service.
```bash
npx prisma service domain add shop.example.com --service web
npx prisma service domain wait shop.example.com --service web --timeout 15m
npx prisma service domain show shop.example.com --service web --json
npx prisma service domain retry shop.example.com --service web
npx prisma service domain delete shop.example.com --service web --confirm shop.example.com
```
`add` verifies DNS first. If the CNAME is not visible yet, it fails with the record to create (pointing at `switchboard.<region>.prisma.build`); create it and run `add` again. `wait --timeout 0` does one status check. States: `pending_dns`, `provisioning_tls`, `active`, `failed`.
## Environment Variables
Three layers: production values, preview values shared by every preview branch, and per-branch overrides on top of preview. Production values cannot be branch-scoped.
```bash
npx prisma project env list --json
npx prisma project env list --role preview --json
npx prisma project env list --branch feat-search --json
npx prisma project env add STRIPE_KEY --role production # value read from the shell environment
npx prisma project env add --file .env.preview --role preview
npx prisma project env add API_BASE_URL=https://staging.example.com --branch staging
npx prisma project env update STRIPE_KEY --role production
npx prisma project env delete STRIPE_KEY --role preview
```
- Every write names its scope: `--role production`, `--role preview`, or `--branch <name>`.
- `add` fails when the key exists in that scope; use `update`.
- Values are write-only. `list` shows names and metadata, never values, and nothing pulls them back into a `.env`.
- Values resolve at deploy time. A change applies on the next deploy (or `promote`).
- Keys match `[A-Z_][A-Z0-9_]*`; values are non-empty, up to 8 KB.
- Passing only `KEY` reads the value from the current environment and keeps it out of shell history.
- `COMPOSER_*` variables belong to the deploy and are rewritten on every deploy. Do not edit them.
## Databases and Buckets
A Composer app gets its databases and buckets from its module. These commands inspect them or create standalone ones.
```bash
npx prisma postgres list --json
npx prisma postgres list --branch feat-search --json
npx prisma postgres show db_123 --json
npx prisma postgres usage db_123 --json
npx prisma postgres connection create db_123 --name ci
npx prisma postgres connection rotate conn_123 --confirm conn_123
npx prisma postgres backup list db_123 --json
npx prisma bucket list --json
npx prisma bucket key create bkt_123 --role read
```
`postgres create`, `postgres connection create`, `postgres connection rotate`, and `bucket key create` print credentials exactly once. Store them in the intended secret store immediately and never echo them back. Deleting a database, restoring a backup, or deleting a bucket destroys data; do it only when the user asks, with the exact id as `--confirm`.
## Branches
```bash
npx prisma branch list --json
```
`branch` only lists. Branches are created by a stage deploy, a pushed Git branch, or `service create --branch`, and removed by deleting the Git branch of a connected repository or by the control API's `destroy`.
## Workspaces
```bash
npx prisma auth whoami --json
npx prisma auth workspace list --json
npx prisma auth workspace use <workspace-id>
npx prisma auth workspace logout <workspace-id>
```
`auth login` adds a session per workspace; `workspace use` switches. While `PRISMA_SERVICE_TOKEN` is set it overrides every stored session. `auth logout` ends all sessions.
## Bad
```bash
npx prisma service logs
```
Fails with `SERVICE.TARGET_REQUIRED`; there is no picker and no `PRISMA_SERVICE_ID` fallback.
## Good
```bash
npx prisma service logs api --project my-app --branch feat-search --tail 200 --json
```
## References
- [Deployments](https://www.prisma.io/docs/compute/deployments)
- [Environment variables](https://www.prisma.io/docs/compute/environment-variables)
- [Domains](https://www.prisma.io/docs/compute/domains)
- [`service` command](https://www.prisma.io/docs/cli/service)
@@ -0,0 +1,101 @@
# runtime-requirements
What a service must do to run on Prisma Compute, and the platform limits it must design around.
## Priority
CRITICAL
## Why It Matters
A deploy can converge cleanly and still serve nothing: a loopback listener is unreachable, a slow handler gets a `504` from the ingress, and a sleeping instance stops background work. None of these show up as deploy errors.
## Runtime
- Services run on **Bun**, in a VM next to their Prisma Postgres database. Write server code for Bun, and bundle with `bun build --target=bun` or another bundler that inlines all dependencies. A build that leaves packages as imports needs `dependencies: "external"` on `node()` (Composer 0.29.0 and later).
- There is no global `Temporal`. A service that reads Prisma ORM `DateTime` columns as `Temporal` values compiles, deploys, then fails on the first read. Import a polyfill at the server entry or use string column types.
## Host and Port
- Listen on `service.port()` (default 3000). The runtime also exports it as `PORT`.
- Bind `0.0.0.0`. Compute routes external HTTP to the VM; a `localhost` or `127.0.0.1` listener looks started but is unreachable.
```ts
Bun.serve({ port: service.port(), hostname: "0.0.0.0", fetch: handler });
```
## 60 Seconds to First Byte
The ingress gives a request 60 seconds to start responding. If nothing comes back, the client gets `504 Gateway Time-out` (a short `openresty` HTML body) and Compute cancels the request. Your handler sees only its abort signal (`req.signal`); no error is logged.
- Respond within 60 seconds. For webhooks, return `200` or `202` before processing.
- Keep in-request timeouts (fetch timeouts, `AbortSignal.timeout`) under 60 seconds so your own error handling runs first.
- Continue longer work after the response with `waitUntil`, or persist a job and process it in steps from a separate trigger. Compute does not run scheduled jobs; the trigger is an external scheduler or the Composer `cron` module.
- The deadline covers only the first bytes. The docs say a response that has started is not cut off; the `prisma-composer-core-concepts` skill warns that the ingress buffers streamed responses. Send the first bytes early and test a streaming route on a stage before relying on it.
## Sleep and Keep-Awake
Idle instances scale to zero: the platform snapshots memory and resumes from it on the next request. Requests are unaffected, but `setTimeout`, `setInterval`, and floating promises can stall mid-flight. Do not build schedulers on `setInterval` inside a service.
`@prisma/compute` keeps an instance awake on a best-effort basis:
```ts
import { KeepAwakeGuard, waitUntil } from "@prisma/compute";
waitUntil(processJob(job), { signal: AbortSignal.timeout(10 * 60_000) });
async function processBatch() {
using guard = new KeepAwakeGuard({ signal: AbortSignal.timeout(30_000) });
await doWork();
}
```
- The `signal` caps how long the instance stays awake; it does not cancel the work.
- Release a manually created guard in `finally` with `guard.release()` where `using` is unavailable.
- Neither primitive makes work durable, retries it, or carries it through a restart or a new deployment.
## Connections
Scale to zero closes idle database connections. A long-lived client that treats a dropped connection as fatal crash-loops through `502`s. Keep pools small and reconnect-friendly, and log instead of exiting:
```ts
const sql = new SQL({ url: db.url, max: 1, idleTimeout: 10 });
process.on("uncaughtException", (err) => console.error("uncaughtException", err));
process.on("unhandledRejection", (err) => console.error("unhandledRejection", err));
```
A call into a sleeping service can get `ECONNRESET` during the cold start; retry it. The generated Composer RPC client already does.
## Limits
- No WebSocket servers.
- No persistent filesystem.
- No multi-region deployments. Each service runs in one region, and a Composer deploy uses the project's region, fixed when the first deploy creates the project: `us-east-1`, `us-west-1`, `eu-west-3`, `eu-central-1`, `ap-northeast-1`, or `ap-southeast-1`.
- A deployed `/rpc/<method>` of a Composer contract returns `401` to anything except the services wired to it. That is the service-key check, not a broken deploy.
## Bad
```ts
app.post("/webhook", async (c) => {
await processForTwoMinutes(await c.req.json());
return c.text("done");
});
```
The caller gets a `504` after 60 seconds, and the instance can sleep while the work continues.
## Good
```ts
app.post("/webhook", async (c) => {
const job = await c.req.json();
waitUntil(processForTwoMinutes(job), { signal: AbortSignal.timeout(5 * 60_000) });
return c.body(null, 202);
});
```
## References
- [Request timeout](https://www.prisma.io/docs/compute/request-timeout)
- [Keeping instances awake](https://www.prisma.io/docs/compute/keeping-instances-awake)
- [Known limitations](https://www.prisma.io/docs/compute/limitations)