Port an existing app
Bring an app you already have to Prisma Composer by declaring it, keeping your server code and your build.
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.
Use with your agent
To delegate the port, run this from your project's root and hand the prompt to your coding agent:
A Node.js or Bun service
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.
A Next.js app
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.
More than one service
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.
Verify the port
Build, then run the app locally before you deploy:
bun run build
bunx prisma@latest 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.
Next steps
- Getting started: the same shape, built from scratch.
- Deploying: take the ported app to production.
