Port an existing app
Bringing an existing app to Composer does not mean rewriting it. The server code you already have stays the server, and you add a service declaration around it. This page covers the three common cases.
To delegate the port, run this from your project's root and hand the prompt to your coding agent:
Port this app to Prisma Composer.
1. Install the Composer skill with `npx skills add prisma/composer`, then read https://www.prisma.io/docs/composer/porting-an-app.md and follow it for this repository.
2. Survey the repo before changing anything and report back: every deployable server, its runtime (Node, Bun, or Next.js), its build command and entry file, every `process.env` read, and any database it connects to. Ask me about anything ambiguous.
3. Declare each server as a service with `compute()`, using the `node` or `nextjs` build adapter as appropriate, and compose them in a single `module.ts`.
4. Replace the `process.env` reads: the listen port becomes `service.port()` bound to `0.0.0.0`, config and credentials become fields of the service input schema, and anything another service provides becomes a typed dependency. Replace the URLs services use to reach each other with contracts.
5. If the app talks to Postgres, declare `postgres()` as a dependency and build the existing client from the injected `db.url` instead of a connection-string variable.
6. Verify with `npx tsc --noEmit`, then build and run `npx prisma dev module.ts` and confirm every service boots with a local URL. Do not deploy; show me the diff first.Add a service.ts with compute({ name, deps, build: node({ module, entry }) }) pointing entry at your built server file, then make three changes to the server itself:
-
Read the port from
service.port(), neverprocess.env, and bind0.0.0.0:Bun.serve({ port: service.port(), hostname: '0.0.0.0', fetch: handler }); -
Replace every other
process.envread. Config and credentials become fields of the service's input schema. Anything another service provides becomes a dependency. If a value differs per stage (an app origin, an external URL), bind it withenvParam. Bind a credential withenvSecret. -
If it talks to Postgres: declare
deps: { db: postgres() }and build your existing client (pg, Bun'sSQL, whatever you use today) from the injecteddb.urlinstead of a connection-string env var. See Databases.
Your build must produce a self-contained entry file, with everything inlined except runtime built-ins. Keep your own build if it already does this. Otherwise, a one-line bun build --target=bun produces one. See Getting started.
Use the nextjs build adapter instead of node. With output: 'standalone' set, next build is the whole build:
import nextjs from '@prisma/composer/nextjs';
import { rpc } from '@prisma/composer/service-rpc';
import { compute } from '@prisma/composer-prisma-cloud';
import { apiContract } from '../api/contract.ts';
export default compute({
name: 'web',
deps: { api: rpc(apiContract) },
build: nextjs({ module: import.meta.url, appDir: '..' }),
});export default { output: 'standalone' };Add nextjsBuild() from @prisma/composer/nextjs/control to the deploy config's extensions:
import { defineConfig } from '@prisma/composer/config';
import { nextjsBuild } from '@prisma/composer/nextjs/control';
import { nodeBuild } from '@prisma/composer/node/control';
import { prismaCloud, prismaState } from '@prisma/composer-prisma-cloud/control';
export default defineConfig({
extensions: [prismaCloud(), nodeBuild(), nextjsBuild()],
state: prismaState(),
});Add export const dynamic = 'force-dynamic' to any page or server action that calls service.load(). The runtime environment does not exist at build time, and Next.js will not re-read it for prerendered routes.
examples/storefront-auth in the Composer repository is a complete example of a ported app: a Next.js frontend calling a Bun API service that owns a Postgres.
Port them into one module.ts and replace the URLs they used to reach each other with contracts. The payoff is that the calls between services become typed, and every environment (production, stages, tests) gets the wiring for free.
Build, then run the app locally before you deploy:
bun run build
bunx prisma dev module.tspnpm run build
pnpm prisma dev module.tsyarn build
yarn prisma dev module.tsnpm run build
npx prisma dev module.tsEvery service should appear in the startup output with a local URL. If a service fails at boot with a module-resolution error, its build left something un-inlined. Check that the entry file is self-contained.
Every service should appear in the startup output with a local URL. If a service fails at boot with a module-resolution error, its build left something un-inlined. Check that the entry file is self-contained.
- Getting started: the same shape, built from scratch.
- Deploying: take the ported app to production.