On this page

Preview branches

Preview branches: a separate database, URL and keys for every feature branch or pull request, built from your migrations.

A preview branch is a separate project that belongs to a production project: its own Postgres database, API URL, keys, Auth users, storage and functions. It starts from production's migrations (or, when production has none yet, its current schema), then runs your seed script. Data is not copied unless you ask: either production's data with personal data masked (below), or a branch you create from Backups › Restore to a point in time › A new branch, which starts with production's data as it was at that moment (see Backups).

PlansPro: 3 branches per project. Scale: 10. Not on Free.
SchemaProduction's migrations in order (or a copy of its schema), then seed.sql
SettingsAuth settings (providers, redirect URLs, e-mails) are copied from production
Limits and usageThe same limits as your plan; branches count towards your account's shared database and file quota
Idle branchesPause after 3 days without requests; restore them from their page. They are not deleted for inactivity.
DeletingDelete a branch any time; deleting the production project deletes its branches

#Create a branch

From the project's Branches page, or with the CLI:

Terminal
stitchdb branches create feature/checkout --wait
stitchdb branches switch feature/checkout   # db push, functions deploy, gen types now use the branch
stitchdb db push
stitchdb branches switch main

The branch switcher under the project name in the dashboard moves between production and its branches.

#Production data, masked

To test with realistic data, choose Copy production data, masked when you create the branch (or stitchdb branches create <name> --with-data). After the migrations, the branch gets production's rows with personal data replaced by fake values:

  • Masking runs inside production's own query, so real values never leave production.
  • Columns are masked by name and type: e-mails, phone numbers, first, last and full names (a plain name column in tables such as users, customers or profiles), usernames, street addresses, cities, postal codes, IP addresses, birth dates (shifted by up to 180 days), and passwords, tokens and API keys (removed). Identity numbers such as iban or tax_id become random digits.
  • The same value gets the same fake value everywhere in one copy: the e-mail in auth.users, customers and orders stays equal, so joins and unique keys keep working. Each new branch gets different fake values.
  • auth.users and auth.identities come along masked: e-mail, phone, names in the profile metadata (other metadata is dropped) and every token. Passwords are removed. With a test password (--test-password), every copied user can sign in with it. Sessions, refresh tokens, MFA factors, audit logs and Storage files are not copied.
  • Rows are loaded without firing triggers or foreign key checks (they were consistent in production), sequences continue where production is, and materialized views are refreshed.

Mark more columns, or keep one as it is, from Branches › Masking rules, or in SQL so the rule lives in your migrations:

SQL
comment on column public.tickets.body is '@mask text';     -- placeholder text
comment on column public.orders.gift_note is '@mask null';  -- removed
RuleBecomes
email, first_name, last_name, full_name, usernameA fake e-mail at example.com or a fake name
phone, street, city, zip, ipA fake phone number (+1 555…), address, city, postal code or private IP address
dateThe date shifted by up to 180 days
text, hash, digits, empty, nullPlaceholder text, a hash (also for uuid), random digits, an empty value or NULL
keepUnchanged (turns off automatic masking for that column)

Rules in the dashboard win over column comments, which win over the automatic choice. Masking is a safeguard for test environments, not anonymisation in the legal sense: free-text and JSON columns can still contain personal data, so check the preview in Masking rules and add rules for them. The copy is limited to the size of production's tables by plan (Data copied into a preview branch or a new project); a branch counts towards your database size like any project.

#Merge

Merging applies the migrations the branch has and production doesn't, in order, on production. Data is not merged.

Terminal
stitchdb branches merge feature/checkout --dry-run
stitchdb branches merge feature/checkout --delete

With the GitHub App or the GitHub workflow, merging the pull request does this for you: the commit on main pushes the migrations to production and the branch is deleted when the pull request closes.

#API

Method and pathDoes
GET /platform/v1/projects/<ref>/branchesList branches
POST /platform/v1/projects/<ref>/branchesCreate: { git_branch, seed?, data?: "masked", test_password? } (returns the existing branch when the name is taken)
GET /platform/v1/projects/<ref>/branches/masking/previewTables, sizes and the columns a masked copy replaces
PUT /platform/v1/projects/<ref>/branches/maskingRules: { rules: { "public.orders.note": "text" } }
GET /platform/v1/branches/<branch-ref>Status: branch_status is creating, ready or failed
POST /platform/v1/branches/<branch-ref>/merge{ dry_run?, delete? }
DELETE /platform/v1/branches/<branch-ref>Delete the branch