Onboarding & offboarding
The lifecycle of a tenant - create, provision, migrate, suspend, and remove.
A tenant's life has a few distinct moments. TenancyJS gives you a command (and a hook) for each, so onboarding and offboarding are scripted and repeatable rather than manual.
From your signup handler: onboardTenant
Doing it all in one call - record → provision → migrate, with automatic rollback if a step fails - is
onboardTenant from tenancyjs-core. Point it at your runtime and call it when a customer signs up:
import { onboardTenant } from "tenancyjs-core";
import runtime from "../tenancy.config"; // your defineTenancyRuntime result
export async function handleSignup(form: SignupForm) {
// Records the tenant, provisions its schema/database, and runs its migrations.
// If provisioning or migration fails, it rolls back (deprovision + delete) and throws,
// so a failed signup never leaves a half-created tenant.
const tenant = await onboardTenant(runtime, { id: form.slug, plan: form.plan });
return tenant;
}Steps whose runtime hook is absent are skipped (row-level has no provisioner). The manual, step-by-step version below is the same lifecycle if you'd rather wire it yourself or drive it from the CLI.
Onboarding
Create the record
Add the tenant to your store - this is the source of truth, including its placement (schema/database) for the isolating strategies.
npx tenancyjs-cli tenant create acme --set plan=proProvision its storage
For schema- or
database-per-tenant, create the schema/database via your
provisioner.provision hook.
npx tenancyjs-cli tenant provision acme(Row-level tenants share tables, so there's nothing to provision.) See the
per-ORM provisioning recipes for the exact create schema/create database
and migrate hooks for your ORM.
Migrate its schema
Bring the tenant's storage up to your current schema through your provisioner.migrate hook.
npx tenancyjs-cli tenant migrate acmeDay-two: rolling out a migration
When you ship a schema change, migrate every tenant. --all reports each tenant's outcome and exits
non-zero if any failed, so it's safe to run in CI:
npx tenancyjs-cli tenant migrate --allSuspending
Suspending flips status in your store; your resolver should treat a suspended tenant as a fail-closed 404, so requests stop immediately.
npx tenancyjs-cli tenant suspend acme
npx tenancyjs-cli tenant activate acme # bring it backOffboarding
Dropping a tenant's storage is destructive, so it always requires an explicit id - never --all.
npx tenancyjs-cli tenant deprovision acme # runs your provisioner.deprovision hookDeprovision drops data. Keep it behind your own confirmation/backup process; TenancyJS makes it explicit (no fan-out), but it won't second-guess a targeted command.