Stages
The Problem
Your team has three environments: development, staging, and production. Each has different requirements:
- Production should be protected from accidental teardowns
- Staging might require specific API keys for integration testing
- Development should default to localhost
Without a shared definition, every developer configures these differently. Someone creates an unprotected production config. Someone else forgets the staging API key. The inconsistency causes problems down the line.
Stages solve this. They're templates defined in your project's settings.yml that every team member shares.
What Stages Are
A stage is a named template for database configs. When you create a config and assign it to a stage, the config inherits the stage's defaults and requirements.
Think of stages as blueprints:
devstage: localhost defaults, no protection neededstagingstage: requires integration API keysprodstage: always protected, cannot be deleted
Your project defines these once in settings.yml. Every developer on the team gets the same blueprints.
Defining Stages
Stages live in .noorm/settings.yml under the stages key:
# .noorm/settings.yml
stages:
dev:
description: Local development database
defaults:
dialect: postgres
host: localhost
port: 5432
staging:
description: Staging environment
defaults:
dialect: postgres
protected: false
secrets:
- key: STRIPE_TEST_KEY
type: api_key
description: Stripe test API key for payment testing
required: true
prod:
description: Production database
locked: true
defaults:
dialect: postgres
protected: trueWhen a developer creates a config from the staging stage, it automatically:
- Uses postgres dialect
- Is not protected (staging data is expendable)
- Requires a
STRIPE_TEST_KEYsecret before builds can run
Stage Properties
Each stage can define these properties:
| Property | Type | Description |
|---|---|---|
description | string | Human-readable description shown in the TUI |
locked | boolean | If true, configs cannot be deleted (default: false) |
defaults | object | Values applied when creating a config from this stage |
secrets | array | Secrets that must be configured |
Defaults
Defaults provide initial values when creating a config. Most can be overridden by the developer in the TUI:
stages:
dev:
defaults:
dialect: postgres
host: localhost
port: 5432
database: myapp_devSome defaults are enforced and cannot be overridden in the TUI:
| Default | Behavior |
|---|---|
protected: true | Cannot be set to false in the TUI |
isTest: true | Cannot be set to false in the TUI |
dialect | Cannot be changed after config creation |
This means if your prod stage sets protected: true, developers cannot create an unprotected production config through the normal workflow.
Manual Override
These enforced defaults can still be changed by manually editing the encrypted state file, but this requires deliberate action. The protection is against accidents, not determined circumvention.
Locked Stages
Setting locked: true prevents configs from being deleted:
stages:
prod:
locked: true
defaults:
protected: trueA locked production config cannot be accidentally removed. The developer must first change the config's stage assignment.
Assigning a Config to a Stage
When you create a new config in the TUI, noorm asks which stage to use:
? Select stage for new config:
dev Local development database
staging Staging environment
prod Production databaseThe config inherits that stage's defaults. The stage selection happens during config creation through the TUI.
Stage-Specific Secrets
Stages can require secrets that get inserted into your database via SQL templates. These are for sensitive values like API keys and encryption keys—not database connection credentials.
stages:
staging:
secrets:
- key: STRIPE_TEST_KEY
type: api_key
description: Stripe test API key for payment testing
required: true
- key: SENDGRID_KEY
type: api_key
description: SendGrid API key for email testing
required: falseSecret types control how the TUI handles input:
| Type | Behavior |
|---|---|
string | Plain text input |
password | Masked input, no echo |
api_key | Masked input, no echo |
connection_string | Plain text, URI validation |
Required secrets must be set before builds can run. If a template references a missing required secret, the build fails with a clear error.
Optional secrets are prompted but can be skipped. Templates that reference them should handle the undefined case.
Universal Secrets
Some secrets apply to all configs regardless of stage. Define these at the settings root level:
# Required by ALL configs
secrets:
- key: ENCRYPTION_KEY
type: password
description: Application encryption key for sensitive data
stages:
prod:
# Additional secrets for prod only
secrets:
- key: AWS_SECRET_KEY
type: api_key
description: AWS credentials for S3 uploadsA production config would need both ENCRYPTION_KEY (universal) and AWS_SECRET_KEY (stage-specific).
Common Stage Patterns
Here are patterns that work well for most teams:
Three-Environment Setup
stages:
dev:
description: Local development
defaults:
dialect: postgres
host: localhost
port: 5432
staging:
description: Staging server
defaults:
dialect: postgres
protected: false
secrets:
- key: STRIPE_TEST_KEY
type: api_key
required: true
prod:
description: Production
locked: true
defaults:
dialect: postgres
protected: true
secrets:
- key: STRIPE_LIVE_KEY
type: api_key
required: trueTest Database Stage
stages:
test:
description: Ephemeral test database
defaults:
dialect: postgres
host: localhost
port: 5432
isTest: trueThe isTest: true flag marks this as a test database. noorm uses this for:
- Conditional build rules (include seed data only in test environments)
- SDK's
requireTestoption (refuses to connect ifisTestis not true) - Preventing accidental test operations against production
What's Next?
- Secrets - Managing sensitive values per stage
- Configs - Creating configs from stages
- Organization - Using build rules with settings.yml
