- Getting Started
- Migrating from an earlier version
- Example
- Code of Conduct
- Contributing
- Support
- License
- Node.js
^20.19 || ^22.12 || >=24.0 - Prisma ORM
7.x, installed in your project (cosmoprism runs your project's Prisma CLI, it does not bundle its own) - a PostgreSQL central database holding one row per tenant
npm install prisma@^7 tsx --save-dev
npm install @futura-dev/cosmoprism --save-devnpx @futura-dev/cosmoprism initinit scaffolds one context per database role β central holds your tenant
table, tenant is the schema replicated across every tenant database:
.cosmoprism.json
prisma/
βββ central/
β βββ prisma.config.ts # schema, migrations and datasource url of the central db
β βββ schema.prisma
β βββ seed.ts
βββ tenant/
βββ prisma.config.ts # same, for a single tenant db
βββ schema.prisma
βββ seed.ts
Three variables are involved, and only the first two are yours to set:
| variable | who sets it | what it points at |
|---|---|---|
TENANT_REGISTRY_DATABASE_URL |
you, in .env |
the database holding your tenant table, which cosmoprism reads to list the tenants |
CENTRAL_DATABASE_URL |
you, in .env |
the central datasource, read by prisma/central/prisma.config.ts |
TENANT_DATABASE_URL |
cosmoprism, at run time | the tenant currently being worked on, read by prisma/tenant/prisma.config.ts |
TENANT_REGISTRY_DATABASE_URL="postgresql://β¦/central"
CENTRAL_DATABASE_URL="postgresql://β¦/central"TENANT_DATABASE_URL is injected into the Prisma process, once per tenant: a
command on ten tenants runs Prisma ten times, each with a different value. Do
not set it in .env β whatever you write there is overwritten for every tenant
command, and it only ever serves as a fallback for scripts you run by hand.
Cosmoprism loads .env before running a command. Prisma ORM 7 does not, so
scripts you invoke yourself, seeds included, have to load it on their own.
Cosmoprism has to know, for each tenant, the url of its database. It reads them
from a table you already own β your tenant registry β and .cosmoprism.json
tells it how that table is shaped:
{
"engine": "postgres",
"tenantTable": {
"name": "tenant",
"idAttributeName": "id",
"databaseUrlAttributeName": "db_url"
}
}| key | meaning |
|---|---|
engine |
the database the registry runs on. postgres is the only supported value |
tenantTable.name |
the table listing your tenants, in the database TENANT_REGISTRY_DATABASE_URL points at |
tenantTable.idAttributeName |
the column identifying a tenant, shown when the CLI asks you to pick one |
tenantTable.databaseUrlAttributeName |
the column holding the connection url of that tenant's database |
So the config above expects a table like:
| id | db_url |
|---|---|
acme |
postgresql://β¦/tenant_acme |
globex |
postgresql://β¦/tenant_globex |
Any other column is ignored: the registry is your table, with your own
constraints and columns, and cosmoprism queries it with pg directly rather
than through Prisma β it needs no schema of its own.
It also does not have to be the central database. Keeping it there is the common
case, and then TENANT_REGISTRY_DATABASE_URL and CENTRAL_DATABASE_URL hold
the same value; point the first somewhere else β a registry shared across
several applications, say β and nothing else changes.
Every command is a Prisma command run once per selected database. No command runs without a target: there is deliberately no implicit "run on everything".
| target | flag |
|---|---|
| central | -c |
| one tenant | -t <tenant-url> |
| pick tenants from a list | -t, with no value |
| every tenant | --all-tenants |
Selectors add up, so -c --all-tenants is the whole system and --all-tenants
alone is every tenant but not central. A -t with a value takes the tenant
connection url, the one in your databaseUrlAttributeName column; a bare
-t lets you pick from the registry instead.
Two commands narrow the rules. migrate deploy runs unattended and never
prompts, so -t requires a value there. studio opens exactly one database, so
its flags are a choice rather than a sum and --all-tenants does not apply.
On generate, validate and format the flags select a schema instead of a
database: the tenant schema is one file shared by every tenant. There -t takes
no value, opens no connection and --all-tenants does not apply.
| command | targets | cosmoprism adds |
|---|---|---|
migrate dev [-n <name>] [--create-only] |
-c -t --all-tenants |
generate + seed after each migration, opted out with --skip-generate / --skip-seed |
migrate deploy |
-c -t <url> --all-tenants |
the whole fleet in one command, without prompting |
migrate reset [-f] |
-c -t --all-tenants |
generate + seed after each reset, same two flags |
migrate status |
-c -t --all-tenants |
one report per database, and exit code 1 if any of them is behind |
migrate diff [--script] [--exit-code] |
-c -t --all-tenants |
--from / --to named once for both contexts |
db seed |
-c -t --all-tenants |
|
generate / validate / format [--check] |
-c -t |
|
studio [-b <browser>] [-p <port>] |
-c or -t |
# create + apply a migration on central, then generate and seed
npx @futura-dev/cosmoprism migrate dev -c -n add_users
# same, on tenants you pick from a list
npx @futura-dev/cosmoprism migrate dev -t -n add_users
# the deploy-time command: central, then every tenant
npx @futura-dev/cosmoprism migrate deploy -c --all-tenants
# one tenant only, by connection url
npx @futura-dev/cosmoprism migrate deploy -t "postgresql://β¦"
# is the whole fleet up to date? exits 1 when at least one database is not
npx @futura-dev/cosmoprism migrate status -c --all-tenants
# what a next migration would have to apply, as SQL
npx @futura-dev/cosmoprism migrate diff -c --script
npx @futura-dev/cosmoprism migrate reset -c -f
npx @futura-dev/cosmoprism db seed --all-tenants
npx @futura-dev/cosmoprism generate -c -t
npx @futura-dev/cosmoprism studio -cmigrate diff names its two ends the same way whatever the target, so one
command reads the same for central and for every tenant:
| source | what it is |
|---|---|
database |
the database of the selected target β one tenant at a time |
schema |
the schema.prisma of the target's context |
migrations |
the state that context's migrations directory builds |
empty |
nothing at all |
It defaults to --from database --to schema, the diff a next migration would
have to apply. --script renders SQL instead of a summary, with each target
announced as a SQL comment so the output stays executable, and --exit-code
returns 2 when at least one selected database has a non-empty diff β which,
together with the exit code of migrate status, is what lets CI fail on a
schema that drifted from its databases.
- A tenant is targeted by exporting
$TENANT_DATABASE_URLto the Prisma process, the one way that works across every Prisma command. Yourprisma/tenant/prisma.config.tsreads it, so nothing else has to know which tenant is running. - Prisma ORM 7 no longer generates the client nor seeds implicitly after a
migration. Cosmoprism chains both steps itself, so
migrate devandmigrate resetleave every selected database ready to use. - Your project's Prisma CLI is the one that runs. Cosmoprism bundles none, and anything it does not cover stays available by calling Prisma directly.
Projects on a cosmoprism release built for Prisma ORM 5/6 have a one-way upgrade
to make: connection urls move to prisma.config.ts, the env variables are
renamed and every command now needs an explicit target. Migrations and databases
are untouched. MIGRATION.md walks through it step by step.
As contributors and maintainers of this open-source web project, we pledge to provide a welcoming and inclusive environment for everyone. We value the participation of individuals from diverse backgrounds and perspectives and aim to foster a respectful and harassment-free community.
To ensure a positive experience for all community members, we have established the following code of conduct that applies to all project-related activities and interactions, both online and offline. By participating in this project, you are expected to uphold these guidelines.
Thank you for considering contributing to this open-source web project! We appreciate your interest and support. To ensure a smooth collaboration process, please follow the guidelines
If you need assistance, have questions, or want to provide feedback related to this open-source web project, there are several ways to get support:
-
Issue Tracker: Check the project's issue tracker on GitHub to see if your question or issue has already been addressed. If not, feel free to open a new issue with a detailed description.
-
Email: You can also reach out to the project maintainers directly via email at opensource@futura-dev.com. Please allow for a reasonable response time.
Before seeking support, make sure to review the project's documentation and readme file, as they may contain helpful information and answers to common questions.
When seeking support or reporting issues, please provide as much relevant information as possible, such as the version of the project you are using, the steps to reproduce the problem, and any error messages encountered. This will help us better understand and assist you with your query.
While we strive to provide timely and helpful support, please note that response times may vary depending on the availability of project maintainers and the complexity of the issue.
We appreciate your interest in this project and look forward to assisting you!
This project is under MIT License. Please check our LICENSE page.