TenancyJS
Guides

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=pro

Provision 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 acme

Day-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 --all

Suspending

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 back

Offboarding

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 hook

Deprovision 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.

On this page