or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

database-operations.mdindex.mdmigrations.mdstatement-operations.mdtypes.md

migrations.mddocs/

0

# Migrations

1

2

SQL-based migration system supporting up/down migrations, rollback functionality, and migration state management for database schema evolution.

3

4

## Capabilities

5

6

### Migration Execution

7

8

Run database migrations with comprehensive configuration options and rollback support.

9

10

```typescript { .api }

11

// Available on Database instance

12

migrate(config?: MigrationParams): Promise<void>;

13

14

interface MigrationParams {

15

/** Force rollback and re-apply the latest migration on each app launch */

16

force?: boolean;

17

/** Migration table name (default: 'migrations') */

18

table?: string;

19

/** Path to migrations folder (default: path.join(process.cwd(), 'migrations')) */

20

migrationsPath?: string;

21

/** Migration data array (if provided, migrationsPath is ignored) */

22

migrations?: readonly MigrationData[];

23

}

24

```

25

26

### Migration File Structure

27

28

Migrations are stored as SQL files with a specific naming convention and structure:

29

30

```typescript { .api }

31

interface MigrationFile {

32

/** Migration ID extracted from filename */

33

id: number;

34

/** Migration name extracted from filename */

35

name: string;

36

/** Full filename */

37

filename: string;

38

}

39

40

interface MigrationData {

41

/** Migration ID */

42

id: number;

43

/** Migration name */

44

name: string;

45

/** SQL for applying the migration */

46

up: string;

47

/** SQL for rolling back the migration */

48

down: string;

49

}

50

```

51

52

**Migration File Naming Convention:**

53

- Files must follow the pattern: `{id}.{name}.sql`

54

- Examples: `001-initial.sql`, `002-add-users-table.sql`, `003-user-indexes.sql`

55

56

**Migration File Content Structure:**

57

```sql

58

-- Up migration (applied when migrating forward)

59

CREATE TABLE users (

60

id INTEGER PRIMARY KEY AUTOINCREMENT,

61

name TEXT NOT NULL,

62

email TEXT UNIQUE NOT NULL,

63

created_at DATETIME DEFAULT CURRENT_TIMESTAMP

64

);

65

66

-- down

67

-- Down migration (applied when rolling back)

68

DROP TABLE users;

69

```

70

71

**Usage Examples:**

72

73

```typescript

74

import { open } from "sqlite";

75

import sqlite3 from "sqlite3";

76

77

const db = await open({

78

filename: "./app.db",

79

driver: sqlite3.Database

80

});

81

82

// Run migrations from default ./migrations directory

83

await db.migrate();

84

85

// Run migrations from custom directory

86

await db.migrate({

87

migrationsPath: "./database/migrations"

88

});

89

90

// Use custom migration table name

91

await db.migrate({

92

table: "schema_versions",

93

migrationsPath: "./migrations"

94

});

95

96

// Force re-apply latest migration (useful for development)

97

await db.migrate({

98

force: true

99

});

100

101

// Use programmatic migration data instead of files

102

const migrationData: MigrationData[] = [

103

{

104

id: 1,

105

name: "initial",

106

up: "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);",

107

down: "DROP TABLE users;"

108

},

109

{

110

id: 2,

111

name: "add-email",

112

up: "ALTER TABLE users ADD COLUMN email TEXT;",

113

down: "ALTER TABLE users DROP COLUMN email;"

114

}

115

];

116

117

await db.migrate({

118

migrations: migrationData

119

});

120

```

121

122

### Migration Directory Setup

123

124

Create a migrations directory with properly structured SQL files:

125

126

```

127

migrations/

128

├── 001-initial.sql

129

├── 002-create-users.sql

130

├── 003-create-posts.sql

131

└── 004-add-indexes.sql

132

```

133

134

**Example Migration Files:**

135

136

`001-initial.sql`:

137

```sql

138

-- Create initial database structure

139

CREATE TABLE schema_info (

140

version INTEGER PRIMARY KEY,

141

applied_at DATETIME DEFAULT CURRENT_TIMESTAMP

142

);

143

144

-- down

145

DROP TABLE schema_info;

146

```

147

148

`002-create-users.sql`:

149

```sql

150

-- Create users table

151

CREATE TABLE users (

152

id INTEGER PRIMARY KEY AUTOINCREMENT,

153

username TEXT UNIQUE NOT NULL,

154

email TEXT UNIQUE NOT NULL,

155

password_hash TEXT NOT NULL,

156

created_at DATETIME DEFAULT CURRENT_TIMESTAMP,

157

updated_at DATETIME DEFAULT CURRENT_TIMESTAMP

158

);

159

160

-- Create trigger to update updated_at

161

CREATE TRIGGER update_users_updated_at

162

AFTER UPDATE ON users

163

BEGIN

164

UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;

165

END;

166

167

-- down

168

DROP TRIGGER IF EXISTS update_users_updated_at;

169

DROP TABLE users;

170

```

171

172

`003-create-posts.sql`:

173

```sql

174

-- Create posts table

175

CREATE TABLE posts (

176

id INTEGER PRIMARY KEY AUTOINCREMENT,

177

title TEXT NOT NULL,

178

content TEXT,

179

author_id INTEGER NOT NULL,

180

published BOOLEAN DEFAULT FALSE,

181

created_at DATETIME DEFAULT CURRENT_TIMESTAMP,

182

updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,

183

FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE

184

);

185

186

-- down

187

DROP TABLE posts;

188

```

189

190

`004-add-indexes.sql`:

191

```sql

192

-- Add performance indexes

193

CREATE INDEX idx_users_email ON users(email);

194

CREATE INDEX idx_users_username ON users(username);

195

CREATE INDEX idx_posts_author ON posts(author_id);

196

CREATE INDEX idx_posts_published ON posts(published);

197

CREATE INDEX idx_posts_created ON posts(created_at);

198

199

-- down

200

DROP INDEX IF EXISTS idx_posts_created;

201

DROP INDEX IF EXISTS idx_posts_published;

202

DROP INDEX IF EXISTS idx_posts_author;

203

DROP INDEX IF EXISTS idx_users_username;

204

DROP INDEX IF EXISTS idx_users_email;

205

```

206

207

### Migration Behavior

208

209

The migration system provides intelligent migration management:

210

211

1. **Automatic Migration Table**: Creates a `migrations` table (or custom name) to track applied migrations

212

2. **Sequential Application**: Applies migrations in ID order, skipping already-applied migrations

213

3. **Rollback Support**: Can rollback migrations that exist in database but not in files

214

4. **Force Mode**: When `force: true`, rolls back and re-applies the latest migration

215

5. **Transaction Safety**: Each migration runs in a transaction - if it fails, changes are rolled back

216

6. **Validation**: Ensures migration IDs are sequential and unique

217

218

### Advanced Migration Patterns

219

220

**Development Workflow with Force Mode:**

221

```typescript

222

// During development, force re-apply latest migration

223

if (process.env.NODE_ENV === 'development') {

224

await db.migrate({ force: true });

225

} else {

226

await db.migrate();

227

}

228

```

229

230

**Multiple Database Migration:**

231

```typescript

232

const databases = await Promise.all([

233

open({ filename: "./users.db", driver: sqlite3.Database }),

234

open({ filename: "./products.db", driver: sqlite3.Database }),

235

open({ filename: "./orders.db", driver: sqlite3.Database })

236

]);

237

238

// Run migrations on all databases

239

await Promise.all(databases.map(db => db.migrate({

240

migrationsPath: "./migrations"

241

})));

242

```

243

244

**Custom Migration Logic:**

245

```typescript

246

// Read and modify migrations programmatically

247

import { readMigrations } from "sqlite/utils/migrate";

248

249

const migrationData = await readMigrations("./migrations");

250

251

// Add environment-specific modifications

252

const modifiedMigrations = migrationData.map(migration => ({

253

...migration,

254

up: process.env.NODE_ENV === 'test'

255

? migration.up.replace('users', 'test_users')

256

: migration.up

257

}));

258

259

await db.migrate({

260

migrations: modifiedMigrations

261

});

262

```

263

264

### Error Handling

265

266

Proper error handling for migration operations:

267

268

```typescript

269

try {

270

await db.migrate();

271

console.log("Migrations completed successfully");

272

} catch (error) {

273

console.error("Migration failed:", error);

274

275

// Migrations are automatically rolled back on failure

276

// Check migration state

277

const appliedMigrations = await db.all(

278

"SELECT id, name FROM migrations ORDER BY id"

279

);

280

console.log("Currently applied migrations:", appliedMigrations);

281

282

throw error; // Re-throw to halt application startup

283

}

284

```

285

286

## Utility Functions

287

288

```typescript { .api }

289

/**

290

* Read migration files from a directory and parse their content

291

* @param migrationPath - Path to migrations directory (default: './migrations')

292

* @returns Promise resolving to array of migration data with parsed up/down SQL

293

*/

294

function readMigrations(migrationPath?: string): Promise<MigrationData[]>;

295

```

296

297

This utility function is available for advanced use cases where you need to read and process migration files programmatically before applying them. The function reads migration files following the `{id}-{name}.sql` naming pattern and parses the SQL content into `up` and `down` sections.

298

299

**Usage Examples:**

300

301

```typescript

302

import { readMigrations } from "sqlite/utils/migrate";

303

304

// Read migrations from default directory

305

const migrations = await readMigrations();

306

console.log(`Found ${migrations.length} migration files`);

307

308

// Read migrations from custom directory

309

const customMigrations = await readMigrations("./database/migrations");

310

311

// Process migrations programmatically

312

const processedMigrations = migrations.map(migration => ({

313

...migration,

314

up: `-- Environment: ${process.env.NODE_ENV}\n${migration.up}`,

315

down: migration.down

316

}));

317

318

// Use with database migrate method

319

await db.migrate({

320

migrations: processedMigrations

321

});

322

```

323

324

**Return Value Structure:**

325

326

Each migration object returned contains:

327

- `id`: Migration ID extracted from filename

328

- `name`: Migration name extracted from filename

329

- `up`: SQL statements for applying the migration (comments removed)

330

- `down`: SQL statements for rolling back the migration (comments removed)