---
title: How to Use TypeORM with Capacitor and SQLite
description: Learn how to use TypeORM with Capacitor and SQLite for decorator-based entity modeling and repository-driven data access in your mobile apps.
date:
  created: 2026-02-27
  updated: 2026-07-17
authors:
  - robingenz
categories:
  - Capacitor
  - Guides
  - SDKs
links:
  - Capacitor SQLite: sdks/capacitor/sqlite.md
faq: true
---

# How to Use TypeORM with Capacitor and SQLite

Many developers already use TypeORM on the backend to manage databases with TypeScript decorators and a familiar repository pattern. The good news: the same approach works in Capacitor apps too. The [Capacitor SQLite plugin](../../sdks/capacitor/sqlite.md) ships with a built-in `SQLiteConnection` class that plugs directly into TypeORM's `DataSource` — no additional adapter package required. This guide walks you through the complete setup, from defining entities to running queries and managing migrations.

For raw API usage, see the [Capacitor SQLite plugin documentation](../../sdks/capacitor/sqlite.md#api).

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="/" target="_blank">
    <img alt="Build and deploy your Capacitor app with Capawesome Cloud" src="https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
  </a>
</div>

## What is TypeORM?

[TypeORM](https://typeorm.io/){:target="_blank"} is one of the most widely used ORMs in the TypeScript ecosystem. It takes a decorator-based approach to database modeling: you define your tables as classes, annotate columns and relationships with decorators, and interact with data through repositories and query builders.

Here's why it pairs well with Capacitor apps:

- **Decorator-based entities** — Tables are modeled as regular TypeScript classes. Columns, primary keys, and relationships are declared with decorators like `@Column()` and `@ManyToOne()`.
- **Repository pattern** — Each entity gets a repository with built-in methods for common operations (`find`, `save`, `remove`), so you rarely need to write SQL.
- **Automatic schema sync** — During development, TypeORM can synchronize your database schema with your entity definitions automatically.
- **Migration support** — For production, TypeORM provides a migration system to apply schema changes incrementally.
- **Wide adoption** — TypeORM has a large community and extensive documentation, which means answers to most questions are a search away.

## Prerequisites

Before you begin, make sure you have a Capacitor project with the [Capacitor SQLite plugin](../../sdks/capacitor/sqlite.md) installed. To install the plugin, please refer to the [Installation](../../sdks/capacitor/sqlite.md#installation) section in the plugin documentation. If you haven't worked with the plugin directly yet, [How to Use SQLite in a Capacitor App](./how-to-use-sqlite-in-a-capacitor-app.md) covers opening a database and running queries before you layer TypeORM on top.

## Installation

Since the TypeORM driver is included in the [Capacitor SQLite plugin](../../sdks/capacitor/sqlite.md) itself, you only need to install TypeORM and its peer dependency:

```bash
npm install typeorm reflect-metadata
```

TypeORM relies on decorators and metadata reflection, so you need to enable both in your `tsconfig.json`:

```json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
```

Finally, import `reflect-metadata` once at the entry point of your app (e.g. `main.ts`), before any other imports:

```typescript
import 'reflect-metadata';
```

## Configuring the DataSource

TypeORM uses a `DataSource` to manage the database connection. To connect it to the [Capacitor SQLite plugin](../../sdks/capacitor/sqlite.md), pass an `SQLiteConnection` instance as the `driver`:

```typescript
import { Sqlite, SQLiteConnection } from '@capawesome-team/capacitor-sqlite';
import { DataSource } from 'typeorm';

const AppDataSource = new DataSource({
  type: 'capacitor',
  driver: new SQLiteConnection(Sqlite),
  database: 'my-app',
  entities: [],
  synchronize: true,
  logging: ['error', 'schema'],
  migrationsRun: false,
});
```

A few things to note here:

- `type: 'capacitor'` tells TypeORM to use its built-in Capacitor driver, which delegates database operations to the provided `driver` instance.
- `driver: new SQLiteConnection(Sqlite)` bridges TypeORM to the Capacitor SQLite plugin. The `SQLiteConnection` class handles opening, closing, and routing queries to the correct database.
- `database` is the name used to identify the database file.
- `synchronize: true` automatically creates and updates tables based on your entities. This is convenient during development but should be disabled in production.
- `migrationsRun: false` is required when using the `capacitor` type.

To initialize the connection when your app starts:

```typescript
await AppDataSource.initialize();
```

## Defining Entities

TypeORM models database tables as classes decorated with `@Entity()`. Each property that maps to a column is annotated with a decorator like `@PrimaryGeneratedColumn()` or `@Column()`.

```typescript
import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
  ManyToOne,
  OneToMany,
  CreateDateColumn,
} from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id!: number;

  @Column('text')
  name!: string;

  @Column({ type: 'text', unique: true })
  email!: string;

  @CreateDateColumn()
  createdAt!: Date;

  @OneToMany(() => Post, (post) => post.author)
  posts!: Post[];
}

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id!: number;

  @Column('text')
  title!: string;

  @Column({ type: 'text', nullable: true })
  content!: string | null;

  @ManyToOne(() => User, (user) => user.posts)
  author!: User;
}
```

A few things to note:

- `@PrimaryGeneratedColumn()` creates an auto-incrementing primary key.
- `@Column()` accepts a type string or an options object for constraints like `unique` and `nullable`.
- `@CreateDateColumn()` automatically sets the current timestamp when a row is inserted.
- `@OneToMany()` and `@ManyToOne()` define the relationship between `User` and `Post`. TypeORM uses these to generate foreign keys and enable eager/lazy loading.

Don't forget to register your entities in the `DataSource` configuration:

```typescript
const AppDataSource = new DataSource({
  // ...
  entities: [User, Post],
});
```

## Working with Repositories

TypeORM's repository pattern provides a high-level API for data access. Each entity gets its own repository with built-in methods for the most common operations.

### Insert

```typescript
const userRepo = AppDataSource.getRepository(User);

const user = userRepo.create({
  name: 'Alice',
  email: 'alice@example.com',
});
await userRepo.save(user);
```

The `create()` method instantiates an entity without persisting it. Calling `save()` writes it to the database and populates the generated `id`.

### Select

```typescript
// Find all users
const allUsers = await userRepo.find();

// Find with a condition
const user = await userRepo.findOneBy({
  email: 'alice@example.com',
});

// Find with relations
const userWithPosts = await userRepo.findOne({
  where: { id: 1 },
  relations: { posts: true },
});
```

### Update

```typescript
await userRepo.update({ id: 1 }, { name: 'Bob' });
```

### Delete

```typescript
await userRepo.delete({ id: 1 });
```

For more complex queries, TypeORM also offers a `QueryBuilder` that supports joins, subqueries, and aggregations:

```typescript
const users = await userRepo
  .createQueryBuilder('user')
  .leftJoinAndSelect('user.posts', 'post')
  .where('user.name LIKE :name', { name: '%Ali%' })
  .getMany();
```

## Transactions

When multiple operations need to succeed or fail as a unit, wrap them in a transaction:

```typescript
await AppDataSource.transaction(async (manager) => {
  const user = manager.create(User, {
    name: 'Alice',
    email: 'alice@example.com',
  });
  await manager.save(user);

  const post = manager.create(Post, {
    title: 'Hello World',
    content: '...',
    author: user,
  });
  await manager.save(post);
});
```

If any operation inside the callback throws, the entire transaction is rolled back. The `manager` parameter is a transactional `EntityManager` — use it instead of individual repositories to ensure all operations run within the same transaction.

## Migrations

While `synchronize: true` is handy during development, it should not be used in production since it can lead to data loss. Instead, use TypeORM's migration system to apply schema changes incrementally.

### Writing Migrations

Create a migration class that implements `up` and `down` methods:

```typescript
import { MigrationInterface, QueryRunner } from 'typeorm';

export class CreateUsers1709000000000 implements MigrationInterface {
  async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      CREATE TABLE IF NOT EXISTS user (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT NOT NULL UNIQUE,
        createdAt DATETIME DEFAULT (datetime('now'))
      )
    `);
  }

  async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`DROP TABLE IF EXISTS user`);
  }
}
```

### Running Migrations

Register your migrations in the `DataSource` and run them at startup:

```typescript
import { CreateUsers1709000000000 } from './migrations/CreateUsers1709000000000';

const AppDataSource = new DataSource({
  type: 'capacitor',
  driver: new SQLiteConnection(Sqlite),
  database: 'my-app',
  entities: [User, Post],
  migrations: [CreateUsers1709000000000],
  synchronize: false,
  migrationsRun: false,
});

await AppDataSource.initialize();
await AppDataSource.runMigrations();
```

TypeORM tracks which migrations have been applied in a `migrations` table, so calling `runMigrations()` repeatedly is safe — only pending migrations are executed.

## FAQ

### Why is `migrationsRun: false` required for the `capacitor` DataSource type?

Because TypeORM's usual startup-time migration runner assumes a Node.js-style synchronous filesystem it can scan for migration files, which doesn't exist in a Capacitor app's WebView context. With `type: 'capacitor'`, you run migrations explicitly by calling `AppDataSource.runMigrations()` yourself after `initialize()`, rather than relying on that automatic startup behavior.

### Is it safe to leave `synchronize: true` on for a production release?

No — this is a common mistake carried over from backend TypeORM usage. `synchronize: true` automatically alters your schema to match your entity definitions, which can silently drop or truncate columns if an entity changes shape between app versions. It's useful for fast iteration during development, but production apps should switch to `synchronize: false` with explicit migrations, since there's no reviewing or rolling back an automatic schema sync that goes wrong on a user's device.

### Do TypeORM relations like `@OneToMany` and `@ManyToOne` work the same as they would with a server-side database?

Yes, at the API level — TypeORM generates the same foreign key relationships and supports the same eager/lazy loading options regardless of which driver backs the `DataSource`. The underlying difference is that queries execute against the on-device SQLite file through the Capacitor SQLite plugin's driver instead of a network connection, but your entity and repository code doesn't need to account for that difference.

### What happens if an error occurs partway through a transaction callback?

The entire transaction rolls back automatically — nothing inside the callback is persisted. This is why the guide's transaction example uses the callback's `manager` parameter instead of the regular repositories: operations run through that transactional `EntityManager` are all tied to the same atomic unit, so a failure on the second `save()` undoes the first one too.

### Do I need a separate adapter package to use TypeORM with Capacitor?

No — this is one of the more common assumptions coming from other ORM integrations. The `SQLiteConnection` class ships inside the Capacitor SQLite plugin itself, so `npm install typeorm reflect-metadata` is the only dependency install needed; there's no third-party Capacitor-TypeORM bridge package to add on top.

## Stay Updated

Want to stay up to date with the latest features and guides? Subscribe to the Capawesome newsletter.

[Subscribe to the Capawesome Newsletter](/newsletter/){ .md-button .md-button--primary }

## Conclusion

TypeORM brings its decorator-based entity modeling and repository pattern to Capacitor apps through the built-in `SQLiteConnection` class in the [Capacitor SQLite plugin](../../sdks/capacitor/sqlite.md). There's no separate adapter to install — just configure a `DataSource` with `type: 'capacitor'`, define your entities as decorated classes, and use repositories for data access.

**Resources:**

- [API Reference](../../sdks/capacitor/sqlite.md#api)
- [Exploring the Capacitor SQLite API](./exploring-the-capacitor-sqlite-api.md)

**Related tutorials:** 

- If you prefer a lighter, SQL-first approach check [Drizzle ORM](./how-to-use-drizzle-orm-with-capacitor-and-sqlite.md) or [Kysely](./how-to-use-kysely-with-capacitor-and-sqlite.md)


If you have questions or feedback, join the [Capawesome Discord](https://discord.gg/VCXxSVjefW){:target="_blank"} server to connect with the community. And subscribe to the Capawesome [newsletter](/newsletter/){:target="_blank"} to stay updated on the latest news.
