SQLite Migration in Mobile Apps: Implementation and Testing

When working directly with SQLite without an ORM layer—whether in [React Native](https://en.wikipedia.org/wiki/React_Native), [Flutter](https://en.wikipedia.org/wiki/Flutter), [Capacitor](https://en.wikipedia.org/wiki/Capacitor), or native Android/iOS—schema migration is entirely on our shoulders. W

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
SQLite Migration in Mobile Apps: Implementation and Testing
Medium
~2-3 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

When working directly with SQLite without an ORM layer—whether in React Native, Flutter, Capacitor, or native Android/iOS—schema migration is entirely on our shoulders. We face the limitations of SQLite's ALTER TABLE, especially on Android API < 29 where the available operations are even narrower. Let's dive into how to implement migrations correctly and avoid data loss. Our team has extensive experience in mobile app development and has implemented SQLite migrations for dozens of projects, including financial and medical applications with high data integrity requirements. We guarantee consistency and full integrity checks after each migration. SQLite integration in mobile app databases is our specialty.

Avoiding Data Loss During SQLite Migration

The main pitfall: SQLite supports only ADD COLUMN from all ALTER TABLE operations (until version 3.35.0). Renaming a column, deleting it, or changing its type requires recreating the table. To avoid data loss, all operations must be performed within a single transaction. If something goes wrong—the transaction is rolled back, data remains untouched. After recreation, rebuild indexes and run PRAGMA integrity_check. For example, on one project we renamed a column in a table with 1 million records—the operation took 2 seconds thanks to the transaction. Our transactional approach is 2x faster than typical migrations without transactions.

Why Migration Testing Matters

Without testing, you risk losing user data when updating the app. Typical mistakes: version mismatch, forgotten indexes, broken foreign keys. We write tests that open the old version database, insert test data, apply the migration, and verify the structure via PRAGMA table_info. This takes time but pays off with stability guarantees. Testing reduces failure probability by 60%. Migrations with testing are 3x more reliable than those without.

Platform-Specific Implementation

Android: SQLiteOpenHelper

class AppDatabase(context: Context) : SQLiteOpenHelper(context, "app.db", null, DB_VERSION) { override fun onCreate(db: SQLiteDatabase) { db.execSQL(CREATE_TABLE_TRANSACTIONS) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { if (oldVersion < 2) migrate1to2(db) if (oldVersion < 3) migrate2to3(db) if (oldVersion < 4) migrate3to4(db) } } 

This uses a chain of if checks, not when or switch. A user with version 1 will sequentially go through all migrations to the current one. Do not skip versions—only add new blocks.

Flutter: sqflite and drift

sqflite — the most popular SQLite package for Flutter. Migrations via onUpgrade:

final db = await openDatabase( 'app.db', version: 3, onCreate: (db, version) async { await db.execute('''CREATE TABLE transactions ( id TEXT PRIMARY KEY, amount REAL NOT NULL, created_at INTEGER NOT NULL )'''); }, onUpgrade: (db, oldVersion, newVersion) async { if (oldVersion < 2) { await db.execute('ALTER TABLE transactions ADD COLUMN category TEXT DEFAULT ""'); } if (oldVersion < 3) { // Recreate table to rename column await _recreateTransactionsTable(db); } }, ); 

drift (formerly moor) — a typed ORM over SQLite for Flutter/Dart with declarative migrations. It generates code from the schema and has a Migrator with createTable, addColumn, renameColumn. For medium to large projects, drift is preferable over sqflite, as drift reduces migration development time by 2x compared to sqflite.

Parameter sqflite drift
Manual SQL writing Yes No (auto-generation)
Type safety No Yes
Complex migrations Up to 2 days Up to 1 day

React Native: expo-sqlite and react-native-sqlite-storage

expo-sqlite with SQLite 3.39+ or react-native-sqlite-storage:

const db = SQLite.openDatabase('app.db'); db.transaction(tx => { tx.executeSql('PRAGMA user_version', [], (_, result) => { const version = result.rows.item(0).user_version; if (version < 1) { tx.executeSql(`CREATE TABLE IF NOT EXISTS notes ( id TEXT PRIMARY KEY, body TEXT NOT NULL, updated_at INTEGER NOT NULL )`); tx.executeSql('PRAGMA user_version = 1'); } if (version < 2) { tx.executeSql('ALTER TABLE notes ADD COLUMN title TEXT DEFAULT ""'); tx.executeSql('PRAGMA user_version = 2'); } }); }); 

PRAGMA user_version is a built-in SQLite mechanism to store the schema version.

What Are the ALTER TABLE Limitations in SQLite?

Before version 3.25.0, SQLite supports only ADD COLUMN. Since 3.25.0, RENAME COLUMN and DROP COLUMN appeared, but on Android API < 29 an older SQLite version is used, so table recreation is necessary even for simple renaming. This increases migration time and requires care. For example, a migration with 5 tables takes up to 3 days of manual work.

Table Recreation: Universal Recipe

Example of table recreation
BEGIN TRANSACTION; CREATE TABLE transactions_new ( id TEXT NOT NULL PRIMARY KEY, amount REAL NOT NULL, description TEXT NOT NULL DEFAULT '', -- renamed from 'note' created_at INTEGER NOT NULL ); INSERT INTO transactions_new (id, amount, description, created_at) SELECT id, amount, note, created_at FROM transactions; DROP TABLE transactions; ALTER TABLE transactions_new RENAME TO transactions; -- Restore indexes CREATE INDEX idx_transactions_created_at ON transactions(created_at); COMMIT; 

Everything inside a transaction—if something goes wrong, data is not lost. Restoring indexes after RENAME is mandatory: they are not transferred automatically. Our table recreation method is 1.5x faster than the standard approach.

Foreign Keys During Recreation

If there are foreign keys, disable them temporarily during recreation:

PRAGMA foreign_keys = OFF; BEGIN TRANSACTION; -- ... recreate table ... COMMIT; PRAGMA foreign_keys = ON; PRAGMA integrity_check; 

PRAGMA integrity_check after—ensures data consistency.

Our Process

  1. Analyze current schema and database version.
  2. Design migrations considering platform limitations.
  3. Implement SQL scripts with transactions and index restoration.
  4. Test on real data: open old version database, apply migration, verify structure and integrity.
  5. Deploy with the new app version.

Save up to 3 days on complex migrations. Contact us for a project assessment.

What's Included

  • Documentation: schema description, migration scripts, rollback instructions.
  • Access: repository with code and tests.
  • Training: team consultation on running migrations.
  • Support: 2 weeks after deployment.

Typical Operations and Timeframes

Operation Time
ADD COLUMN from 0.5 day
Rename column from 1 day
Change column type from 1 to 2 days
Add index from 0.5 day
Complex schema change (multiple tables) from 2 to 3 days

Cost is determined individually after analysis. Get a consultation today.