8.0 KiB
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.
Requirements
- Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer (
engines.nodeis^22.18.0 || ^24.11.0 || >=26.0.0since Composer 0.30.0). Composer loads the.tsentry directly; older versions stop atERR_UNKNOWN_FILE_EXTENSION. - Bun. Deployed services run on Bun, and
bun build --target=bunis the simplest way to produce the single-file entry a service needs. - The packages:
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 if prisma.config.ts fails to evaluate.
The Four Files
// src/server.ts
import service from "./service.ts";
Bun.serve({
port: service.port(),
hostname: "0.0.0.0",
fetch: () => new Response("ok"),
});
// 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" }),
});
// module.ts
import { module } from "@prisma/composer";
import web from "./src/service.ts";
export default module("my-app", ({ provision }) => {
provision(web);
});
// 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:
{
"scripts": {
"build": "bun build src/server.ts --target=bun --outfile dist/server.mjs"
}
}
Rules:
entryanddirresolve relative to the service module (module: import.meta.url), not the project root.- The
composersection accepts onlyextensionsandstate. AddnextjsBuild()from@prisma/composer/nextjs/controlwhen a service uses thenextjsadapter. prisma.config.tsis read by the CLI only; app code never imports it. A separateprisma-composer.config.tsis refused.- Inside the module graph, write relative imports with explicit
.tsextensions and enableallowImportingTsExtensionsintsconfig.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 })andprovision()ids allow[A-Za-z0-9]only. A hyphenated id passestscand then fails at load. The platform rejects provision ids shorter than three characters, so name a databasedatabase, notdb.
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().
// 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" }),
});
// 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 } });
});
// 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 withdataContract. It delivers{ url, client }with a client typed by your Prisma ORM data contract, and the deploy applies the committedmigrations/before the service starts. Every schema change goes throughprisma contract emitandprisma migration planfirst. See Databases.postgresis not exported from the package root in 0.28.0, andpostgres()with no argument does not declare a plain database. UserawPostgres()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.
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
// 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.