nself v0.2.0 introduces powerful database management capabilities that streamline your development workflow. Built around schema-first development with DBML (Database Markup Language), these tools provide everything you need to design, migrate, seed, and maintain your PostgreSQL database.
The nself database management system provides:
When you run nself init
, the database management system is automatically configured. Your project structure will include:
my-project/
├── .env.local
├── database/
│ ├── schema.dbml # Your database schema
│ ├── migrations/ # Generated SQL migrations
│ │ ├── 001_initial.up.sql
│ │ └── 001_initial.down.sql
│ └── seeds/ # Seed data files
│ ├── development/
│ ├── staging/
│ └── production/
└── docker-compose.yml
Edit database/schema.dbml
to define your database structure:
// Users table
Table users {
id uuid [pk, default: `gen_random_uuid()`]
email varchar(255) [unique, not null]
password_hash varchar(255) [not null]
first_name varchar(100)
last_name varchar(100)
is_active boolean [default: true]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
}
// Posts table
Table posts {
id uuid [pk, default: `gen_random_uuid()`]
title varchar(255) [not null]
content text
author_id uuid [ref: > users.id]
is_published boolean [default: false]
published_at timestamp
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
}
// Comments table
Table comments {
id uuid [pk, default: `gen_random_uuid()`]
content text [not null]
author_id uuid [ref: > users.id]
post_id uuid [ref: > posts.id]
created_at timestamp [default: `now()`]
}
// Indexes
TableGroup blog_tables {
users
posts
comments
}
Check the current state of your database:
nself db status
Output shows:
Create SQL migrations from your DBML schema:
# Generate migrations from schema changes
nself db run
# Generate with custom message
nself db run --message "Add user profiles table"
# Dry run (preview without creating files)
nself db run --dry-run
This command:
schema.dbml
with the last applied migrationdatabase/migrations/
Execute pending migrations:
# Apply all pending migrations
nself db migrate:up
# Apply specific number of migrations
nself db migrate:up --steps 1
# Apply migrations to specific version
nself db migrate:up --to 003
# Force apply (skip confirmations)
nself db migrate:up --force
Roll back applied migrations:
# Rollback last migration
nself db migrate:down
# Rollback specific number of migrations
nself db migrate:down --steps 2
# Rollback to specific version
nself db migrate:down --to 001
# Rollback all migrations (reset database)
nself db migrate:down --all
Seed data can be provided in JSON or SQL format:
JSON Format (database/seeds/development/users.json
):
[
{
"email": "admin@example.com",
"password_hash": "$2b$10$...",
"first_name": "Admin",
"last_name": "User",
"is_active": true
},
{
"email": "user@example.com",
"password_hash": "$2b$10$...",
"first_name": "Test",
"last_name": "User",
"is_active": true
}
]
SQL Format (database/seeds/development/posts.sql
):
INSERT INTO posts (title, content, author_id, is_published) VALUES
('Welcome Post', 'Welcome to our blog!', (SELECT id FROM users WHERE email = 'admin@example.com'), true),
('Getting Started', 'Here''s how to get started...', (SELECT id FROM users WHERE email = 'admin@example.com'), true);
# Seed database with current environment data
nself db seed
# Seed with specific environment
nself db seed --env staging
# Seed specific tables
nself db seed --tables users,posts
# Clear existing data before seeding
nself db seed --fresh
# Create backup with automatic naming
nself db backup
# Create backup with custom name
nself db backup --name "pre-migration-backup"
# Create compressed backup
nself db backup --compress
# Include seed data in backup
nself db backup --include-seeds
Backups are stored in database/backups/
with timestamps.
# List available backups
nself db backup:list
# Restore latest backup
nself db restore
# Restore specific backup
nself db restore --name "backup-2025-08-06-14-30-00"
# Restore with confirmation prompt
nself db restore --interactive
Reset your database to a clean state:
# Drop all tables and recreate from schema
nself db reset
# Reset and apply seeds
nself db reset --seed
# Reset without confirmation (dangerous!)
nself db reset --force
Configure different behaviors for each environment:
# Development environment (default)
nself db migrate:up
# Staging environment
ENVIRONMENT=staging nself db migrate:up
# Production environment (requires confirmation)
ENVIRONMENT=production nself db migrate:up
nself generates migrations using intelligent templates:
-- Generated from DBML
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(100),
last_name VARCHAR(100),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT now(),
updated_at TIMESTAMP DEFAULT now()
);
-- Add indexes
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_created_at ON users(created_at);
-- Foreign key constraints
ALTER TABLE posts
ADD CONSTRAINT fk_posts_author_id
FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE;
-- Junction tables for many-to-many relationships
CREATE TABLE IF NOT EXISTS post_tags (
post_id UUID REFERENCES posts(id) ON DELETE CASCADE,
tag_id UUID REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (post_id, tag_id)
);
Configure database management in your .env.local
:
# Database connection
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=myapp
POSTGRES_USER=postgres
POSTGRES_PASSWORD=secure_password
# Migration settings
DB_MIGRATION_TABLE=schema_migrations
DB_MIGRATION_LOCK_TIMEOUT=60
DB_MIGRATION_STATEMENT_TIMEOUT=300
# Backup settings
DB_BACKUP_RETENTION_DAYS=30
DB_BACKUP_COMPRESSION=true
DB_BACKUP_LOCATION=./database/backups
# Development settings
DB_AUTO_MIGRATE=false
DB_CONFIRM_DESTRUCTIVE=true
DB_SEED_ON_INIT=true
nself validates your DBML schema for:
# Validate schema without generating migrations
nself db validate
# Validate with detailed output
nself db validate --verbose
# Check for breaking changes
nself db validate --check-breaking
nself automatically configures Hasura to work with your database schema:
When you run migrations, nself can optionally:
# Apply migrations and update Hasura metadata
nself db migrate:up --update-hasura
# Generate Hasura permissions from schema comments
nself db hasura:permissions
# Export Hasura metadata
nself db hasura:export
Define permissions in your DBML schema:
Table users {
id uuid [pk]
email varchar [unique]
// hasura:permissions user:select[id,email] admin:*
// hasura:relationship posts[author_id->id]
}
Production deployments include additional safety measures:
created_at
and updated_at
timestampsnself provides comprehensive logging for database operations:
# View database operation logs
nself logs db
# Monitor migration progress
nself db migrate:up --verbose
# Check migration history
nself db history
# Analyze schema changes
nself db diff
# Check for conflicting migrations
nself db status
# Resolve conflicts manually
nself db migrate:resolve
# Reset migration state (dangerous!)
nself db migrate:reset-state
# Common DBML syntax errors
Error: Invalid reference syntax
Solution: Use correct ref syntax: [ref: > table.column]
Error: Circular reference detected
Solution: Review table relationships for circular dependencies
Error: Unknown data type
Solution: Use PostgreSQL-compatible data types
# Test database connection
nself db ping
# Check connection parameters
nself db info
# Reset connection pool
nself db reconnect
Enable detailed logging for troubleshooting:
# Enable debug mode for database operations
export NSELF_DEBUG=true
# Run commands with verbose output
nself db migrate:up --debug
# View internal SQL queries
nself db migrate:up --show-sql
Complete example for a blog application:
// Blog schema with users, posts, comments, and tags
// Users table
Table users {
id uuid [pk, default: `gen_random_uuid()`]
email varchar(255) [unique, not null]
username varchar(50) [unique, not null]
password_hash varchar(255) [not null]
first_name varchar(100)
last_name varchar(100)
bio text
avatar_url varchar(500)
is_active boolean [default: true]
is_verified boolean [default: false]
role user_role [default: 'user']
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
Indexes {
(email) [unique]
(username) [unique]
(is_active)
(created_at)
}
}
// Posts table
Table posts {
id uuid [pk, default: `gen_random_uuid()`]
title varchar(255) [not null]
slug varchar(255) [unique, not null]
content text
excerpt text
author_id uuid [ref: > users.id, not null]
category_id uuid [ref: > categories.id]
featured_image_url varchar(500)
is_published boolean [default: false]
is_featured boolean [default: false]
published_at timestamp
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
Indexes {
(slug) [unique]
(author_id)
(category_id)
(is_published, published_at)
(is_featured)
(created_at)
}
}
// Categories table
Table categories {
id uuid [pk, default: `gen_random_uuid()`]
name varchar(100) [unique, not null]
slug varchar(100) [unique, not null]
description text
color varchar(7) // Hex color code
created_at timestamp [default: `now()`]
Indexes {
(slug) [unique]
(name)
}
}
// Tags table
Table tags {
id uuid [pk, default: `gen_random_uuid()`]
name varchar(50) [unique, not null]
slug varchar(50) [unique, not null]
created_at timestamp [default: `now()`]
Indexes {
(slug) [unique]
(name)
}
}
// Post-Tag junction table
Table post_tags {
post_id uuid [ref: > posts.id]
tag_id uuid [ref: > tags.id]
Indexes {
(post_id, tag_id) [pk]
(tag_id)
}
}
// Comments table
Table comments {
id uuid [pk, default: `gen_random_uuid()`]
content text [not null]
author_id uuid [ref: > users.id, not null]
post_id uuid [ref: > posts.id, not null]
parent_id uuid [ref: > comments.id] // For nested comments
is_approved boolean [default: false]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
Indexes {
(post_id, created_at)
(author_id)
(parent_id)
(is_approved)
}
}
// User roles enum
Enum user_role {
user
author
editor
admin
}
// Table groups for organization
TableGroup user_management {
users
}
TableGroup content_management {
posts
categories
tags
post_tags
comments
}
// E-commerce schema with products, orders, and inventory
Table products {
id uuid [pk, default: `gen_random_uuid()`]
name varchar(255) [not null]
slug varchar(255) [unique, not null]
description text
short_description varchar(500)
sku varchar(100) [unique, not null]
price decimal(10,2) [not null]
compare_price decimal(10,2)
cost_price decimal(10,2)
track_inventory boolean [default: true]
inventory_quantity integer [default: 0]
low_stock_threshold integer [default: 5]
weight decimal(8,2)
is_active boolean [default: true]
is_featured boolean [default: false]
category_id uuid [ref: > product_categories.id]
brand_id uuid [ref: > brands.id]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
}
Table product_categories {
id uuid [pk, default: `gen_random_uuid()`]
name varchar(100) [not null]
slug varchar(100) [unique, not null]
description text
parent_id uuid [ref: > product_categories.id]
sort_order integer [default: 0]
is_active boolean [default: true]
created_at timestamp [default: `now()`]
}
Table orders {
id uuid [pk, default: `gen_random_uuid()`]
order_number varchar(50) [unique, not null]
customer_id uuid [ref: > customers.id, not null]
status order_status [default: 'pending']
subtotal decimal(10,2) [not null]
tax_amount decimal(10,2) [default: 0]
shipping_amount decimal(10,2) [default: 0]
total decimal(10,2) [not null]
currency varchar(3) [default: 'USD']
payment_status payment_status [default: 'pending']
fulfillment_status fulfillment_status [default: 'unfulfilled']
notes text
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
}
Enum order_status {
pending
confirmed
processing
shipped
delivered
cancelled
refunded
}
Enum payment_status {
pending
paid
partially_paid
refunded
partially_refunded
}
Enum fulfillment_status {
unfulfilled
partially_fulfilled
fulfilled
shipped
delivered
}
Now that you understand database management, explore:
The database management system in nself v0.2.0 provides everything you need for professional database development, from initial design to production maintenance.