Databases
Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations.
There are two ways for a service to get a Prisma Postgres database, depending on how much you want the framework to do. Both are dependencies: the service declares the need, the module provisions the resource, and your code receives the result from service.load().
Composer's service contracts describe an RPC API between services. A Prisma ORM data contract describes a database schema. This page uses both: postgres() ties a database dependency to a Prisma ORM data contract.
rawPostgres(): bring your own client
Declare rawPostgres() when you want to build the client yourself. The dependency delivers connection config, { url }, and nothing else. In your server entry, build the client you already know (pg, Bun's SQL, an ORM):
import { compute, rawPostgres } from '@prisma/composer-prisma-cloud';
export default compute({
name: 'auth',
deps: { db: rawPostgres() },
// ...
});import { SQL } from 'bun';
const { db } = service.load();
const sql = new SQL({ url: db.url, max: 1, idleTimeout: 10 });Those pool settings matter. Prisma Compute scales to zero and closes idle connections, so keep the pool small and reconnect-friendly. See Deploying.
The module that owns the database provisions it:
const db = provision(rawPostgres({ name: 'database' }));
provision(authService, { deps: { db } });postgres(): a Prisma ORM-typed database
If you want typed queries and managed migrations, make the database a Prisma ORM one. load() then returns { url, client }: the raw connection string, plus a client generated from your data contract. Queries like db.client.orm.public.Product.where({ id }).first() are compile-time checked, with no SQL strings and no row mapping. The client is constructed only on first access, so a service that brings its own Postgres client can still read db.url and keep contract-checked wiring and deploy-time migrations.
The schema workflow runs once per schema change, using the Prisma ORM CLI commands:
- Edit
contract.prisma, your schema. - Run
contract emitto regeneratecontract.jsonandcontract.d.tsfrom it. - Run
migration planto author the migration intomigrations/. - Deploy. The deploy applies
migrations/before the service starts. There is noCREATE TABLE IF NOT EXISTSanywhere in app code.
In your app, wrap the emitted contract once. Both the resource and every service that queries it reference that one value:
import { dataContract } from '@prisma/composer-prisma-cloud/orm';
import type { Contract } from '../contract.d.ts';
import contractJson from '../contract.json' with { type: 'json' };
export const catalogData = dataContract<Contract>(contractJson);You call postgres() on both sides of the wiring, and what you pass it tells the two apart. Passing the contract alone declares the dependency, the service saying what it queries:
deps: { db: postgres(catalogData) }Passing an options object declares the resource. The module that owns the database provisions it, naming the prisma.config.ts path (relative to the module file) so the deploy can reload the emitted contract.json and find migrations/:
const db = provision(
postgres({ name: 'database', contract: catalogData, config: './prisma.config.ts' }),
);Because both ends share the contract value, the deploy refuses to wire a service against a database whose schema does not match. Deploy state records only the contract's identity; the full emitted contract is reloaded through prisma.config.ts at deploy time, and if contract.json is missing, unreadable, or no longer matches the declared contract, the deploy fails before touching the database.
For complete working versions, see examples/orm-demo (one service, one typed database) and examples/store (the full pattern inside a reusable Module) in the Composer repository.
Local databases
dev provisions real local Postgres instances for both kinds, so you can migrate and query them exactly as you would in production, with no cloud credentials. The data persists across restarts until you pass --fresh. See Local development.
Next steps
- Object storage: the other stateful resource, an S3-compatible bucket.
- Apps and Modules: keep a database private inside a Module.
- Prisma ORM CLI reference: the contract and migration commands in detail.
