|
| 1 | +# Migrations |
| 2 | + |
| 3 | +Generate and execute database migrations directly from your TypeScript models with full type safety and dialect support. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The migration system automatically generates SQL DDL statements from your model definitions, supporting incremental migrations and full schema generation. It handles table creation, column additions, foreign keys, indexes, and more across PostgreSQL, MySQL, and SQLite. |
| 8 | + |
| 9 | +## Features |
| 10 | + |
| 11 | +- **Model-driven**: Generate migrations from your TypeScript model definitions |
| 12 | +- **Multi-dialect**: Support for PostgreSQL, MySQL, and SQLite |
| 13 | +- **Incremental**: Smart diff-based migrations that only apply changes |
| 14 | +- **Full schema**: Option to generate complete schema SQL |
| 15 | +- **State tracking**: Maintains migration state for incremental updates |
| 16 | +- **Type safe**: Full TypeScript support with inferred types |
| 17 | + |
| 18 | +## Basic Usage |
| 19 | + |
| 20 | +### Generate and Apply Migration |
| 21 | + |
| 22 | +```ts |
| 23 | +import { generateMigration, executeMigration } from 'bun-query-builder' |
| 24 | + |
| 25 | +// Generate migration from models directory |
| 26 | +const migration = await generateMigration('./models', { |
| 27 | + dialect: 'postgres', |
| 28 | + apply: true, |
| 29 | + full: true |
| 30 | +}) |
| 31 | + |
| 32 | +// Execute the migration |
| 33 | +await executeMigration(migration) |
| 34 | +``` |
| 35 | + |
| 36 | +### Migration Options |
| 37 | + |
| 38 | +```ts |
| 39 | +interface MigrateOptions { |
| 40 | + dialect?: 'postgres' | 'mysql' | 'sqlite' // Default: 'postgres' |
| 41 | + state?: string // Path to state file |
| 42 | + apply?: boolean // Execute SQL immediately |
| 43 | + full?: boolean // Force full schema generation |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +## CLI Commands |
| 48 | + |
| 49 | +### migrate |
| 50 | + |
| 51 | +Generate SQL migrations from models: |
| 52 | + |
| 53 | +```bash |
| 54 | +# Basic migration generation |
| 55 | +query-builder migrate ./app/Models |
| 56 | + |
| 57 | +# With specific dialect |
| 58 | +query-builder migrate ./app/Models --dialect mysql |
| 59 | + |
| 60 | +# Apply migration immediately |
| 61 | +query-builder migrate ./app/Models --apply |
| 62 | + |
| 63 | +# Force full schema generation |
| 64 | +query-builder migrate ./app/Models --full |
| 65 | + |
| 66 | +# Custom state file location |
| 67 | +query-builder migrate ./app/Models --state ./migrations/state.json |
| 68 | +``` |
| 69 | + |
| 70 | +### Examples |
| 71 | + |
| 72 | +```bash |
| 73 | +# Generate PostgreSQL migration for User models |
| 74 | +query-builder migrate ./app/Models --dialect postgres |
| 75 | + |
| 76 | +# Apply MySQL migration immediately |
| 77 | +query-builder migrate ./app/Models --dialect mysql --apply |
| 78 | + |
| 79 | +# Generate full schema for SQLite |
| 80 | +query-builder migrate ./app/Models --dialect sqlite --full |
| 81 | +``` |
| 82 | + |
| 83 | +## Migration Types |
| 84 | + |
| 85 | +### Incremental Migrations |
| 86 | + |
| 87 | +By default, the system generates incremental migrations that only apply changes: |
| 88 | + |
| 89 | +- Creates new tables |
| 90 | +- Adds new columns |
| 91 | +- Adds new foreign keys |
| 92 | +- Adds new indexes |
| 93 | +- Maintains existing data |
| 94 | + |
| 95 | +### Full Schema Migrations |
| 96 | + |
| 97 | +Use the `--full` flag to generate complete schema SQL: |
| 98 | + |
| 99 | +- Drops and recreates all tables |
| 100 | +- Useful for development/testing |
| 101 | +- **Warning**: Destructive operation |
| 102 | + |
| 103 | +## State Management |
| 104 | + |
| 105 | +The migration system maintains state in a JSON file (default: `.qb-migrations.<dialect>.json`) that tracks: |
| 106 | + |
| 107 | +- Current schema plan |
| 108 | +- Migration hash for change detection |
| 109 | +- Last update timestamp |
| 110 | + |
| 111 | +### State File Location |
| 112 | + |
| 113 | +```ts |
| 114 | +// Default location |
| 115 | +const defaultStatePath = join(dir, `.qb-migrations.${dialect}.json`) |
| 116 | + |
| 117 | +// Custom location |
| 118 | +const migration = await generateMigration('./models', { |
| 119 | + state: './custom/path/state.json' |
| 120 | +}) |
| 121 | +``` |
| 122 | + |
| 123 | +## Model Inference |
| 124 | + |
| 125 | +Migrations are generated by analyzing your model definitions: |
| 126 | + |
| 127 | +```ts |
| 128 | +// Example model that generates migration |
| 129 | +const models = defineModels({ |
| 130 | + User: { |
| 131 | + name: 'User', |
| 132 | + table: 'users', |
| 133 | + primaryKey: 'id', |
| 134 | + attributes: { |
| 135 | + id: { validation: { rule: {} } }, |
| 136 | + name: { validation: { rule: {} } }, |
| 137 | + email: { validation: { rule: {} } }, |
| 138 | + active: { validation: { rule: {} } } |
| 139 | + } |
| 140 | + } |
| 141 | +}) |
| 142 | +``` |
| 143 | + |
| 144 | +### Supported Types |
| 145 | + |
| 146 | +The system automatically infers SQL types from your validation rules: |
| 147 | + |
| 148 | +- `string` → `varchar(255)` / `text` |
| 149 | +- `boolean` → `boolean` / `tinyint(1)` |
| 150 | +- `integer` → `integer` |
| 151 | +- `bigint` → `bigint` |
| 152 | +- `float` → `real` |
| 153 | +- `date` → `date` |
| 154 | +- `datetime` → `timestamp` / `datetime` |
| 155 | +- `json` → `jsonb` / `json` / `text` |
| 156 | + |
| 157 | +## Advanced Features |
| 158 | + |
| 159 | +### Foreign Key Relationships |
| 160 | + |
| 161 | +```ts |
| 162 | +const models = defineModels({ |
| 163 | + Post: { |
| 164 | + name: 'Post', |
| 165 | + table: 'posts', |
| 166 | + attributes: { |
| 167 | + user_id: { |
| 168 | + validation: { rule: {} }, |
| 169 | + references: { table: 'users', column: 'id' } |
| 170 | + } |
| 171 | + } |
| 172 | + } |
| 173 | +}) |
| 174 | +``` |
| 175 | + |
| 176 | +### Indexes and Constraints |
| 177 | + |
| 178 | +```ts |
| 179 | +const models = defineModels({ |
| 180 | + User: { |
| 181 | + name: 'User', |
| 182 | + table: 'users', |
| 183 | + attributes: { |
| 184 | + email: { |
| 185 | + validation: { rule: {} }, |
| 186 | + unique: true |
| 187 | + }, |
| 188 | + created_at: { |
| 189 | + validation: { rule: {} }, |
| 190 | + index: { name: 'created_at_idx' } |
| 191 | + } |
| 192 | + } |
| 193 | + } |
| 194 | +}) |
| 195 | +``` |
| 196 | + |
| 197 | +## Error Handling |
| 198 | + |
| 199 | +### Migration Failures |
| 200 | + |
| 201 | +```ts |
| 202 | +try { |
| 203 | + const migration = await generateMigration('./models', { |
| 204 | + dialect: 'postgres', |
| 205 | + apply: true |
| 206 | + }) |
| 207 | + |
| 208 | + await executeMigration(migration) |
| 209 | +} catch (err) { |
| 210 | + console.error('Migration failed:', err) |
| 211 | + // Handle rollback or retry logic |
| 212 | +} |
| 213 | +``` |
| 214 | + |
| 215 | +### Common Issues |
| 216 | + |
| 217 | +- **Dialect mismatch**: Ensure state file dialect matches current dialect |
| 218 | +- **Permission errors**: Check database connection and privileges |
| 219 | +- **Syntax errors**: Verify model definitions and validation rules |
| 220 | + |
| 221 | +## Best Practices |
| 222 | + |
| 223 | +### Development Workflow |
| 224 | + |
| 225 | +1. **Start with full migration**: Use `--full` for initial schema setup |
| 226 | +2. **Use incremental for changes**: Let the system detect and apply only changes |
| 227 | +3. **Version control state files**: Commit migration state files to track schema evolution |
| 228 | +4. **Test migrations**: Always test migrations in development before production |
| 229 | + |
| 230 | +### Production Considerations |
| 231 | + |
| 232 | +- **Backup first**: Always backup before applying migrations |
| 233 | +- **Review SQL**: Check generated SQL before applying |
| 234 | +- **Rollback plan**: Have a strategy for migration failures |
| 235 | +- **State consistency**: Ensure state files are properly managed across environments |
| 236 | + |
| 237 | +## Integration Examples |
| 238 | + |
| 239 | +### With Build Scripts |
| 240 | + |
| 241 | +```json |
| 242 | +{ |
| 243 | + "scripts": { |
| 244 | + "migrate": "query-builder migrate ./src/models --apply", |
| 245 | + "migrate:dev": "query-builder migrate ./src/models --dialect postgres --full", |
| 246 | + "migrate:test": "query-builder migrate ./test/models --dialect sqlite --full" |
| 247 | + } |
| 248 | +} |
| 249 | +``` |
| 250 | + |
| 251 | +### With CI/CD |
| 252 | + |
| 253 | +```yaml |
| 254 | +# GitHub Actions example |
| 255 | +- name: Run migrations |
| 256 | + run: | |
| 257 | + query-builder migrate ./src/models --dialect postgres --apply |
| 258 | +``` |
| 259 | +
|
| 260 | +### With Testing |
| 261 | +
|
| 262 | +```ts |
| 263 | +// In test setup |
| 264 | +beforeAll(async () => { |
| 265 | + const result = await generateMigration('./test/models', { |
| 266 | + dialect: 'postgres', |
| 267 | + full: true |
| 268 | + }) |
| 269 | + |
| 270 | + if (result.sqlStatements.length > 0) { |
| 271 | + await executeMigration(result) |
| 272 | + } |
| 273 | +}) |
| 274 | +``` |
| 275 | + |
| 276 | +## API Reference |
| 277 | + |
| 278 | +### generateMigration |
| 279 | + |
| 280 | +```ts |
| 281 | +function generateMigration( |
| 282 | + dir: string, |
| 283 | + opts?: MigrateOptions |
| 284 | +): Promise<GenerateMigrationResult> |
| 285 | +``` |
| 286 | + |
| 287 | +**Parameters:** |
| 288 | + |
| 289 | +- `dir`: Path to models directory |
| 290 | +- `opts`: Migration options |
| 291 | + |
| 292 | +**Returns:** |
| 293 | + |
| 294 | +- `sql`: Complete SQL string |
| 295 | +- `sqlStatements`: Array of individual SQL statements |
| 296 | +- `hasChanges`: Whether any changes were detected |
| 297 | +- `plan`: Generated migration plan |
| 298 | + |
| 299 | +### executeMigration |
| 300 | + |
| 301 | +```ts |
| 302 | +function executeMigration( |
| 303 | + migration: GenerateMigrationResult |
| 304 | +): Promise<boolean> |
| 305 | +``` |
| 306 | + |
| 307 | +**Parameters:** |
| 308 | + |
| 309 | +- `migration`: Result from generateMigration |
| 310 | + |
| 311 | +**Returns:** |
| 312 | + |
| 313 | +- `boolean`: Success status |
| 314 | + |
| 315 | +## Related |
| 316 | + |
| 317 | +- [Query Builder](./builder.md) - Build type-safe queries |
| 318 | +- [Relations](./relations.md) - Handle model relationships |
| 319 | +- [CLI](./cli.md) - Command-line interface |
| 320 | +- [Configuration](../config.md) - Global settings and options |
0 commit comments