Skip to main content
Prisma Documentation Docs

Search documentation

Type to search this documentation.

On this pageOverview

Deploy the full Prisma stack

This tutorial takes you through the whole recommended stack in one sitting. Prisma Composer declares your app: its services, its databases, and how they connect. Prisma ORM types your data. Prisma Postgres stores it, locally while you develop and on the platform when you deploy. One declaration drives everything: the same module.ts runs the app on your machine and deploys it to Prisma Compute. Plan on about 15 minutes.

It uses the hono template so you get a small API you can verify with curl at every step. The same journey works for the other templates; the framework guides cover each one.

  • Node.js 22.18 or newer (on the 24 line, 24.11 or newer; 24 recommended), or Bun
  • A Prisma Data Platform account for the deploy step, free to create
  • No database needed: local runs provision a local Prisma Postgres, and deploys provision a real one, both from your Composer declaration

If you would rather hand the work to a coding agent, this prompt runs the same journey as the one on the getting started page, with the hono template chosen for you:

The prompt uses npm and Node.js. On Bun, replace npm create prisma@latest -- with bun create prisma@latest, npx with bunx, and npm run with bun run.

with
Create a new Hono API composed with Prisma Composer and Prisma ORM, run it locally, and deploy it to Prisma Compute.

1. Scaffold: `npm create prisma@latest -- my-app --template hono --provider postgres --yes`. Then run `npx prisma@latest init` in `my-app` to confirm the Prisma agent skills the scaffold installed are current, and use them.

2. Read `module.ts` and `service.ts` first: the Composer module provisions the database and the service, and it is what `dev` and `deploy` operate on.

3. Build and run locally: `npm run build`, then `npx prisma dev module.ts`. This provisions a local Prisma Postgres database and applies the contract; no DATABASE_URL is needed. Sample users are seeded on the app's first query. Verify the local URL that `dev` prints: its /users endpoint returns the seeded users.

4. Deploy: check `npx prisma auth whoami`; if I am not signed in, stop and ask me to run `npx prisma auth login` (it opens a browser). The scaffold already wrote the baseline migration under `migrations/app/`; note its directory name, which ends in `_init`. Then run `npx prisma deploy module.ts` and verify the live URL's /users endpoint with curl. The deploy creates the project and provisions the database from the module declaration; do not pass a DATABASE_URL.

5. Evolve the schema: add `role String @default("member")` to the User model in `src/prisma/contract.prisma`, run `npx prisma contract emit`, add `"role"` to the typed select and the returned object in `src/prisma/users.ts`, and plan the migration with `npx prisma migration plan --name add-user-role --from <the init migration directory>`. Then run `npm run build` and `npx prisma deploy module.ts` again, and verify the live /users now returns `role: "member"` with the same createdAt values as before.

One command creates a Composer-declared app with Prisma ORM wired in:

bun create prisma@latest my-app --template hono --provider postgres --no-deploy
Bash
pnpm create prisma@latest my-app --template hono --provider postgres --no-deploy
Bash
yarn create prisma@latest my-app --template hono --provider postgres --no-deploy
Bash
npm create prisma@latest -- my-app --template hono --provider postgres --no-deploy

Answer the prompts for contract authoring style, package manager, and agent skills, or pass the --authoring, --package-manager, and --skills flags to skip them (see the create-prisma reference; Deno projects cannot deploy to Prisma Compute yet, so this tutorial uses Node.js or Bun). Then enter the project:

Answer the prompts for contract authoring style, package manager, and agent skills, or pass the --authoring, --package-manager, and --skills flags to skip them (see the create-prisma reference; Deno projects cannot deploy to Prisma Compute yet, so this tutorial uses Node.js or Bun). Then enter the project:

cd my-app
Bash
cd my-app
Bash
cd my-app
Bash
cd my-app

If you work with a coding agent, run npx prisma@latest init once. The scaffold ran it already, so on a fresh project init confirms the setup and reports each step as already done: the Prisma agent skills that ship inside the Prisma packages are synced, and package.json has a postinstall hook (prisma skills sync || exit 0) that resyncs them on every install, plus a skills:sync script for refreshing them by hand. Running init again is what repairs the setup after you upgrade a Prisma package. See skills.

If you work with a coding agent, run npx prisma@latest init once. The scaffold ran it already, so on a fresh project init confirms the setup and reports each step as already done: the Prisma agent skills that ship inside the Prisma packages are synced, and package.json has a postinstall hook (prisma skills sync || exit 0) that resyncs them on every install, plus a skills:sync script for refreshing them by hand. Running init again is what repairs the setup after you upgrade a Prisma package. See skills.

Start with the declaration, because it is what every later command operates on. The scaffold declares the whole app in two files. module.ts is the app: it provisions a Prisma Postgres database and the service, and wires one to the other:

title="module.ts"
import { module } from "@prisma/composer";

import { postgres } from "@prisma/composer-prisma-cloud/orm";

import { appContract } from "./src/prisma/composer.ts";

import app from "./service.ts";

export default module("my-app", ({ provision }) => {

  const database = provision(

    postgres({

      name: "database",

      contract: appContract,

      config: "./prisma.config.ts",

    }),

    { id: "database" },

  );

  provision(app, { deps: { database } });

});

service.ts declares the service itself: its name, its dependency on the database, and how it is built:

title="service.ts"
import node from "@prisma/composer/node";

import { compute } from "@prisma/composer-prisma-cloud";

import { postgres } from "@prisma/composer-prisma-cloud/orm";

import { appContract } from "./src/prisma/composer.ts";

export default compute({

  name: "app",

  deps: {

    database: postgres(appContract),

  },

  build: node({ module: import.meta.url, entry: "./dist/server.mjs" }),

});

Notice there is no connection string anywhere. The database is a declared dependency, typed by your contract, and Composer injects the connection wherever the app runs. The declaration is ordinary TypeScript: npx tsc --noEmit checks the wiring, and mistakes fail the compile instead of a deploy. Composer covers the model in full: modules, services, dependencies, and the first-party building blocks.

The data side lives under src/prisma/. Your schema is the starter contract in src/prisma/contract.prisma; npm run contract:emit compiles it into the contract artifacts your queries are type-checked against, and src/prisma/db.ts is the typed client the route handlers import. The /users route in src/index.ts is ordinary Hono code calling an ordinary Prisma ORM query.

Change the contract when you are ready to model your own data, re-emit it, and the compiler walks you through every query the change touches. The fundamentals cover the query patterns.

Build the server, then bring the whole declaration up on your machine:

bun run build

bunx prisma dev module.ts
Bash
pnpm run build
pnpm prisma dev module.ts
Bash
yarn build
yarn prisma dev module.ts
Bash
npm run build
npx prisma dev module.ts

dev provisions a local Prisma Postgres database, applies the contract to it, starts the service, and prints the app's local URL when everything is ready. No account, credentials, or connection string are involved; see Local development for how the local platform works.

Sample users are seeded automatically the first time the app queries the database. Confirm the API serves the seeded rows. dev picks a free port and prints the URL, so use the one it printed if it differs from the 3000 shown here:

Bash
curl http://localhost:3000/users
no-copy
[
  { "id": "1", "email": "alice@prisma.io", "username": "alice", "name": "Alice", "createdAt": "2026-08-24 13:51:34.797+00" },
  { "id": "2", "email": "bob@prisma.io", "username": "bob", "name": "Bob", "createdAt": "2026-08-24 13:51:34.803+00" },
  { "id": "3", "email": "carol@prisma.io", "username": "carol", "name": "Carol", "createdAt": "2026-08-24 13:51:34.804+00" }
]

If you prefer the framework's own dev server, npm run dev runs it directly. Composer does not manage that mode, so the app needs a database of your own in DATABASE_URL. Create one inside your project with the CLI:

dev provisions a local Prisma Postgres database, applies the contract to it, starts the service, and prints the app's local URL when everything is ready. No account, credentials, or connection string are involved; see Local development for how the local platform works.

Sample users are seeded automatically the first time the app queries the database. Confirm the API serves the seeded rows. dev picks a free port and prints the URL, so use the one it printed if it differs from the 3000 shown here:

curl http://localhost:3000/users
[

  { "id": "1", "email": "alice@prisma.io", "username": "alice", "name": "Alice", "createdAt": "2026-08-24 13:51:34.797+00" },

  { "id": "2", "email": "bob@prisma.io", "username": "bob", "name": "Bob", "createdAt": "2026-08-24 13:51:34.803+00" },

  { "id": "3", "email": "carol@prisma.io", "username": "carol", "name": "Carol", "createdAt": "2026-08-24 13:51:34.804+00" }

]

If you prefer the framework's own dev server, npm run dev runs it directly. Composer does not manage that mode, so the app needs a database of your own in DATABASE_URL. Create one inside your project with the CLI:

bunx prisma auth login

bunx prisma project create my-app

bunx prisma postgres create mydb
Bash
pnpm prisma auth login
pnpm prisma project create my-app
pnpm prisma postgres create mydb
Bash
yarn prisma auth login
yarn prisma project create my-app
yarn prisma postgres create mydb
Bash
npx prisma auth login
npx prisma project create my-app
npx prisma postgres create mydb

postgres create prints the connection string once; export it as DATABASE_URL, and mint another later with npx prisma postgres connection create mydb if you need one. The db:* scripts in package.json initialize and verify that database. See postgres.

postgres create prints the connection string once; export it as DATABASE_URL, and mint another later with npx prisma postgres connection create mydb if you need one. The db:* scripts in package.json initialize and verify that database. See postgres.

Sign in once (it opens your browser):

bunx prisma auth login
Bash
pnpm prisma auth login
Bash
yarn prisma auth login
Bash
npx prisma auth login

Deployed databases evolve through the migrations you commit, so the first migration must create your starting schema. The scaffold already planned it for you as migrations/app/<timestamp>_init; commit it with your code. You will chain the next migration from it in step 7.

Build and deploy the same declaration:

Deployed databases evolve through the migrations you commit, so the first migration must create your starting schema. The scaffold already planned it for you as migrations/app/<timestamp>_init; commit it with your code. You will chain the next migration from it in step 7.

Build and deploy the same declaration:

bun run build

bunx prisma deploy module.ts
Bash
pnpm run build
pnpm prisma deploy module.ts
Bash
yarn build
yarn prisma deploy module.ts
Bash
npm run build
npx prisma deploy module.ts

Composer creates a project named after your module, provisions the Prisma Postgres database declared in module.ts, wires it to the service, and starts the app. There is nothing to configure and no environment file to pass, because the deployed database comes from the declaration exactly as the local one did. When everything is up, the deploy prints what it made, each part of your app next to the platform resource it became, along with the public URL:

no-copy
my-app
├─ database   postgres-database db_abc123
└─ app        compute-service cps_abc123
              https://xyz.ewr.prisma.build

[!WARNING] The module name must be unique in your workspace

deploy looks the module name up in your workspace and reuses the project this module deployed before rather than creating another. If a my-app project exists whose hosted state the CLI cannot verify (one deployed from a different checkout, for example), the deploy stops with HostedStateBootstrapError and names a project id you did not choose. Deploy under a different name with --name, or rename the module in module.ts:

bun

```bash
bunx prisma deploy module.ts --name my-app-tutorial
```

pnpm

```bash
pnpm prisma deploy module.ts --name my-app-tutorial
```

yarn

```bash
yarn prisma deploy module.ts --name my-app-tutorial
```

npm

```bash
npx prisma deploy module.ts --name my-app-tutorial
```

Composer creates a project named after your module, provisions the Prisma Postgres database declared in module.ts, wires it to the service, and starts the app. There is nothing to configure and no environment file to pass, because the deployed database comes from the declaration exactly as the local one did. When everything is up, the deploy prints what it made, each part of your app next to the platform resource it became, along with the public URL:

my-app

├─ database   postgres-database db_abc123

└─ app        compute-service cps_abc123

              https://xyz.ewr.prisma.build
curl https://xyz.ewr.prisma.build/users

The same three users come back, now served from production next to your database, seeded on the deployed app's first query. Re-deploying is idempotent: build again, deploy again, and the platform applies only the difference. That includes removals, because your module is the source of truth: delete a provision from module.ts, deploy again, and the resource disappears from the platform. See Removing resources.

Live apps outgrow their starter schema, so give users a role. Add one line to the contract:

title="src/prisma/contract.prisma"
// use prisma-8

model User {

  id        Int      @id @default(autoincrement())

  email     String   @unique

  username  String?

  name      String?

  role      String   @default("member")

  posts     Post[]

  createdAt TimestamptzString @default(now())

  updatedAt temporal.updatedAtString()

}

Re-emit the contract, then plan the migration that carries the change, chaining from the baseline migration you committed in step 5:

bunx prisma contract emit

bunx prisma migration plan --name add-user-role --from <timestamp>_init
Bash
pnpm run build
pnpm prisma deploy module.ts
Bash
yarn build
yarn prisma deploy module.ts
Bash
npm run build
npx prisma deploy module.ts

The deploy applies the committed migration to the deployed database in place. The same users come back with role: "member", backfilled by the column default, and their createdAt timestamps unchanged; nothing was dropped or recreated:

Bash
curl https://xyz.ewr.prisma.build/users
no-copy
[
  { "id": "1", "email": "alice@prisma.io", "username": "alice", "name": "Alice", "role": "member", "createdAt": "2026-08-24 18:40:06.375+00" }
]

--from names the migration you are building on, and here it is required: a Composer deploy never sets the db ref that migration plan chains from by default, so a plan without it would describe every table again. The migration plan and migration ref references cover the default, the ref, and how to keep plans chaining on their own.

The plan is your change and nothing else. Review it like any other diff, with migration show or by reading the generated package:

ALTER TABLE "public"."User" ADD COLUMN "role" text DEFAULT 'member' NOT NULL

Emitting also updated the query types, so surface the new field in the route's typed select in src/prisma/users.ts, adding "role" to the .select(...) list and role: user.role to the returned object:

title="src/prisma/users.ts"
const users = await db.orm.public.User.select("id", "email", "username", "name", "role", "createdAt").limit(limit).all();

This is the Prisma ORM loop: the contract is the source of truth, the emit step regenerates the types, and the compiler points at every query the change touches.

If you want to see the change on your machine first, run npm run build and npx prisma dev module.ts again; the local database applies the new migration on start and /users returns the role there too. Then build and deploy again:

title="bun"
bun run build

bunx prisma deploy module.ts
pnpm
pnpm prisma project delete <project-id> --confirm <project-id>
yarn
yarn prisma project delete <project-id> --confirm <project-id>
npm
npx prisma project delete <project-id> --confirm <project-id>

The deploy applies the committed migration to the deployed database in place. The same users come back with role: "member", backfilled by the column default, and their createdAt timestamps unchanged; nothing was dropped or recreated:

curl https://xyz.ewr.prisma.build/users
[

  { "id": "1", "email": "alice@prisma.io", "username": "alice", "name": "Alice", "role": "member", "createdAt": "2026-08-24 18:40:06.375+00" }

]

The project keeps running until you remove it. Deleting it removes the service and the database, so the command asks you to repeat the project id (find it with npx prisma project list):

bunx prisma project delete <project-id> --confirm <project-id>
Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu