Skip to main content

Database Migrations Guide

This guide documents the database schema changes and migration process for Hyperscape.

Overview

Hyperscape uses Drizzle ORM for database schema management and migrations. The database stores persistent game data including characters, inventory, skills, bank, and world state. Database: PostgreSQL (production), SQLite (local development fallback) Migration Tool: Drizzle Kit Migration Location: packages/server/src/database/migrations/

Recent Migrations

Migration 0029: Crafting Skill (PR #698)

Date: 2026-01-30 Changes:
Purpose: Add crafting skill columns to support leather armor, jewelry, and gem cutting. Impact:
  • All existing characters get crafting level 1, XP 0
  • New characters start with crafting level 1, XP 0
  • No data loss or character reset required

Migration 0030: Fletching Skill (PR #699)

Date: 2026-01-31 Changes:
Purpose: Add fletching skill columns to support bow and arrow crafting. Impact:
  • All existing characters get fletching level 1, XP 0
  • New characters start with fletching level 1, XP 0
  • No data loss or character reset required

Migration 0031: Runecrafting Skill (PR #703)

Date: 2026-01-31 Changes:
Purpose: Add runecrafting skill columns to support rune creation at altars. Impact:
  • All existing characters get runecrafting level 1, XP 0
  • New characters start with runecrafting level 1, XP 0
  • No data loss or character reset required

Complete Skill Schema

The characters table now includes 17 skills (34 columns total):

Combat Skills (14 columns)

Note: Constitution starts at level 10 with 1154 XP (OSRS-accurate).

Gathering Skills (6 columns)

Production Skills (14 columns)

Running Migrations

Automatic Migration (Development)

Migrations run automatically when the server starts in development mode:
The server checks for pending migrations and applies them on startup.

Manual Migration (Production)

For production deployments, run migrations manually before deploying:

Generate New Migration

When you modify the schema in packages/server/src/database/schema.ts:
This creates a new migration file in packages/server/src/database/migrations/.

Push Schema Changes (Development Only)

For rapid iteration in development, push schema changes directly without migrations:
⚠️ Warning: This bypasses migrations and can cause data loss. Only use in development.

Migration Best Practices

1. Always Use IF NOT EXISTS

2. Provide Default Values

All new columns should have sensible defaults:

3. Test Migrations Locally First

Before deploying:
  1. Reset local database:
  2. Start fresh and verify migration:
  3. Check migration logs in console
  4. Verify schema in database:

4. Keep Migrations Small and Focused

Each migration should do ONE thing:
  • ✅ Add crafting skill columns
  • ❌ Add crafting skill columns + modify inventory table + add new items

5. Document Breaking Changes

If a migration requires data transformation or has breaking changes, document it:

Schema Conventions

Naming

  • Tables: Lowercase, plural (e.g., characters, items, bank_items)
  • Columns: camelCase (e.g., craftingLevel, fletchingXp)
  • Indexes: {table}_{column}_idx (e.g., characters_username_idx)
  • Foreign Keys: {table}_{column}_fkey (e.g., bank_items_characterId_fkey)

Skill Columns

Each skill has two columns:
  • {skill}Level - Current level (1-99)
  • {skill}Xp - Current XP (0-200,000,000)
Example:

Data Types

  • Levels: integer (1-99)
  • XP: integer (0-200,000,000)
  • IDs: text (UUIDs or custom IDs)
  • Timestamps: timestamp (with timezone)
  • Booleans: boolean
  • JSON: jsonb (for complex data like equipment stats)

Migration History

Skills Migrations

Other Notable Migrations

Rollback Procedure

Drizzle does not support automatic rollbacks. To rollback a migration:

1. Identify the Migration

Find the migration file in packages/server/src/database/migrations/.

2. Write Reverse SQL

Create a reverse migration manually:

3. Apply Reverse Migration

4. Update Migration Journal

Edit packages/server/src/database/migrations/meta/_journal.json to remove the migration entry. ⚠️ Warning: Manual rollbacks can cause data loss. Always backup before rolling back.

Adding a New Skill

To add a new skill to the database:

1. Update Schema

Edit packages/server/src/database/schema.ts:

2. Generate Migration

This creates a new migration file like 0032_add_herblore_skill.sql.

3. Review Migration

Check the generated SQL:

4. Test Migration

5. Update Repository Code

Update CharacterRepository.ts to load/save new skill:

6. Commit Migration

Commit both the schema change and generated migration:

Database Reset (Development)

To completely reset your local database:
⚠️ Warning: This deletes ALL local data (characters, inventory, bank, progress).

Production Deployment

Pre-Deployment Checklist

  • All migrations tested locally
  • Schema changes reviewed
  • Default values set for new columns
  • Backward compatibility verified
  • Rollback plan documented

Deployment Steps

  1. Backup Database:
  2. Run Migrations:
  3. Verify Schema:
  4. Deploy Application:
  5. Monitor Logs: Check for migration errors or schema issues.

Rollback Plan

If deployment fails:
  1. Restore Database:
  2. Revert Code:
  3. Redeploy Previous Version

Migration Troubleshooting

Migration Already Applied

Symptom: Migration fails with “column already exists” error. Cause: Migration was already applied but journal not updated. Fix: Use IF NOT EXISTS in migrations (already done for all skill migrations).

Migration Fails Midway

Symptom: Some columns added, others failed. Cause: SQL error in migration file. Fix:
  1. Check error message for specific SQL issue
  2. Fix migration file
  3. Manually drop partially-added columns
  4. Re-run migration

Schema Out of Sync

Symptom: Code expects columns that don’t exist in database. Cause: Migrations not run after pulling updates. Fix:
Or reset database (development only):

Wrong Default Value

Symptom: New characters have incorrect starting skill levels. Cause: Default value in migration doesn’t match game logic. Fix: Create a new migration to update the default:

Schema Documentation

Characters Table

Primary Key: id (text, UUID) Core Columns:
  • username - Character name (unique)
  • userId - Owner’s user ID (foreign key)
  • createdAt - Creation timestamp
  • lastLogin - Last login timestamp
  • isAgent - Boolean flag for AI agents
Position Columns:
  • x, y, z - World position (real)
  • rotationX, rotationY, rotationZ, rotationW - Quaternion rotation
Combat Columns:
  • health - Current HP (integer)
  • maxHealth - Max HP (integer)
  • combatLevel - Calculated combat level (integer)
  • attackStyle - Current attack style (text)
  • autoRetaliate - Auto-retaliate flag (boolean)
Skill Columns: 34 columns (17 skills × 2 columns each) Equipment Columns:
  • equippedWeapon, equippedShield, equippedHelmet, etc. (text, item IDs)
Misc Columns:
  • coins - Coin pouch amount (integer)
  • avatarUrl - VRM avatar URL (text)
  • walletAddress - Blockchain wallet (text)
  • templateConfig - AI agent template (jsonb)

Inventory Table

Primary Key: id (text, UUID) Columns:
  • characterId - Owner character ID (foreign key)
  • itemId - Item type ID (text)
  • quantity - Stack size (integer, >= 1)
  • slot - Inventory slot (integer, 0-27)
  • metadata - Item metadata (jsonb)
Constraints:
  • Unique: (characterId, slot) - One item per slot
  • Check: quantity >= 1 - No zero-quantity items

Bank Items Table

Primary Key: id (text, UUID) Columns:
  • characterId - Owner character ID (foreign key)
  • itemId - Item type ID (text)
  • quantity - Stack size (integer)
  • slot - Bank slot (integer, 0-479)
  • tab - Bank tab (integer, 0-8)
  • isPlaceholder - Placeholder flag (boolean)
Constraints:
  • Unique: (characterId, slot) - One item per slot

Drizzle Kit Commands

Generate Migration

Create a new migration from schema changes:
Output: New migration file in src/database/migrations/

Apply Migrations

Run pending migrations:

Push Schema (Dev Only)

Push schema changes directly without migrations:
⚠️ Warning: Bypasses migrations, can cause data loss.

Introspect Database

Generate schema from existing database:

Studio (Database GUI)

Open Drizzle Studio to browse database:
Opens web UI at https://local.drizzle.studio

Environment Variables

Development

Default: Uses Docker PostgreSQL container (auto-started by server).

Production

Providers:
  • Neon - Serverless PostgreSQL
  • Supabase - PostgreSQL with extras
  • Railway - PostgreSQL + hosting
  • Fly.io - PostgreSQL + hosting

Migration File Format

File Naming

Format: {number}_{description}.sql Examples:
  • 0029_add_crafting_skill.sql
  • 0030_add_fletching_skill.sql
  • 0031_add_runecrafting_skill.sql

File Structure

Note: --> statement-breakpoint is required between statements for Drizzle to parse correctly.

Meta Files

Drizzle generates metadata files in migrations/meta/:
  • _journal.json - Migration history
  • {number}_snapshot.json - Schema snapshot after migration
Do not edit these files manually - they are auto-generated.

Common Migration Patterns

Add Skill Columns

Add Table

Add Index

Add Constraint

Modify Column

Data Integrity

Foreign Keys

All foreign keys use ON DELETE CASCADE to maintain referential integrity:
Effect: When a character is deleted, all related data (inventory, bank, etc.) is automatically deleted.

Constraints

Inventory Quantity:
Effect: Prevents zero-quantity items in inventory. Bank Slot Range:
Effect: Enforces 480-slot bank limit.

Performance Considerations

Indexes

Add indexes for frequently queried columns:

JSONB Columns

Use JSONB for complex data that doesn’t need relational queries:
Benefits:
  • Flexible schema
  • No joins required
  • GIN indexes for fast queries
Drawbacks:
  • Can’t enforce constraints
  • Harder to query specific fields

See Also