---
description: Learn how to use Drizzle ORM with the Capacitor SQLite plugin to build type-safe database layers in your cross-platform mobile apps.
title: How to Use Drizzle ORM with Capacitor and SQLite - Capawesome
image: https://capawesome.io/docs/assets/images/social/blog/how-to-use-drizzle-orm-with-capacitor-and-sqlite.png
---

[ Skip to content](#how-to-use-drizzle-orm-with-capacitor-and-sqlite) 

[ 🔐 Introducing the **Capacitor Vault** plugin — store secrets behind biometrics or a device passcode.](/blog/announcing-the-capacitor-vault-plugin/) 

* [  Formbricks ](/docs/plugins/formbricks/)
* [  Geocoder ](/docs/plugins/geocoder/)
* [  Google Sign-In ](/docs/plugins/google-sign-in/)
* [  Grafana Faro ](/docs/plugins/grafana-faro/)
* [  libSQL ](/docs/plugins/libsql/)
* [  Live Update ](/docs/plugins/live-update/)
* [  Managed Configurations ](/docs/plugins/managed-configurations/)
* [  Media Session ](/docs/plugins/media-session/)
* [  ML Kit ](/docs/plugins/mlkit/)
* [  Navigation Bar ](/docs/plugins/navigation-bar/)
* [  NFC ](/docs/plugins/nfc/)
* [  OAuth ](/docs/plugins/oauth/)
* [  Pedometer ](/docs/plugins/pedometer/)
* [  Photo Editor ](/docs/plugins/photo-editor/)
* [  PostHog ](/docs/plugins/posthog/)
* [  Printer ](/docs/plugins/printer/)
* [  Purchases ](/docs/plugins/purchases/)
* [  RealtimeKit ](/docs/plugins/realtimekit/)
* [  Screen Orientation ](/docs/plugins/screen-orientation/)
* [  Screenshot ](/docs/plugins/screenshot/)
* [  Secure Preferences ](/docs/plugins/secure-preferences/)
* [  Speech Recognition ](/docs/plugins/speech-recognition/)
* [  Speech Synthesis ](/docs/plugins/speech-synthesis/)
* [  Share Target ](/docs/plugins/share-target/)
* [  Square Mobile Payments ](/docs/plugins/square-mobile-payments/)
* [  SQLite ](/docs/plugins/sqlite/)
* [  Superwall ](/docs/plugins/superwall/)
* [  Torch ](/docs/plugins/torch/)
* [  Vault ](/docs/plugins/vault/)
* [  Wifi ](/docs/plugins/wifi/)
* [  Zip ](/docs/plugins/zip/)
* [  Cloud ](/docs/cloud/)
* [  Live Updates ](/docs/cloud/live-updates/)
* Advanced
* Integrations
* [  Native Builds ](/docs/cloud/native-builds/)
* [  Configuration ](/docs/cloud/native-builds/configuration/)
* [  Environments ](/docs/cloud/native-builds/environments/)
* Guides
* [  Sample Projects ](/docs/cloud/native-builds/sample-projects/)
* [  Troubleshooting ](/docs/cloud/native-builds/troubleshooting/)
* [  Automations ](/docs/cloud/automations/)
* [  Assist ](/docs/cloud/assist/)
* Account
* Organizations
* [  Organization and User Management ](/docs/cloud/organizations/memberships/)
* [  Single Sign-On (SSO) ](/docs/cloud/organizations/sso/)
* [  Teams ](/docs/cloud/organizations/teams/)
* [  Two-Factor Authentication ](/docs/cloud/organizations/two-factor-authentication/)
* [  Integrations ](/docs/cloud/integrations/)
* [  License Keys ](/docs/cloud/license-keys/)
* [  Webhooks ](/docs/cloud/webhooks/)
* [  Pricing ](https://capawesome.io/pricing/)
* [  FAQ ](/docs/cloud/faq/)
* [  Support ](/docs/cloud/support/)
* [  Contributing ](/docs/contributing/)
* [  LLMs ](/docs/llms/)
* [  Insiders ](/docs/insiders/)
* [  License ](https://capawesome.io/legal/eula/)
* [  Support ](/docs/insiders/support/)
* [  FAQ ](/docs/insiders/faq/)
* [  Blog ](/blog/)
* Categories

* [  Relational Queries ](#relational-queries)
* [  Transactions ](#transactions)
* [  Migrations ](#migrations)
* [  Stay Updated ](#stay-updated)
* [  Conclusion ](#conclusion)

* Related links

# How to Use Drizzle ORM with Capacitor and SQLite[¶](#how-to-use-drizzle-orm-with-capacitor-and-sqlite "Permanent link")

If you're building a Capacitor app that needs a local database, you've probably dealt with writing raw SQL strings and mapping results manually. It works, but it's error-prone and doesn't scale well. Drizzle ORM offers a better approach: a lightweight, type-safe ORM that lets you define your schema in TypeScript and write queries that feel like SQL — with full autocompletion and compile-time checks. In this guide, you'll learn how to set up Drizzle ORM with the [Capacitor SQLite plugin](/docs/plugins/sqlite/) using the new `@capawesome/capacitor-sqlite-drizzle` adapter. For the **Capacitor SQLite plugin** itself, see the [plugin documentation](/docs/plugins/sqlite/#api).

[ ![Build and deploy your Capacitor app with Capawesome Cloud](../../assets/external/cloud.capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.69628c3f.png) ](/) 

## What is Drizzle ORM?[¶](#what-is-drizzle-orm "Permanent link")

[Drizzle ORM](https://orm.drizzle.team/) is a TypeScript ORM designed to be lightweight, type-safe, and close to SQL. Unlike heavier ORMs that abstract SQL away entirely, Drizzle uses a query API that mirrors SQL syntax. If you know SQL, you already know most of Drizzle.

Here's what makes it a good fit for Capacitor apps:

* **Type safety** — Your schema is defined in TypeScript, so queries are checked at compile time. No more runtime surprises from mistyped column names.
* **No code generation** — Unlike some ORMs, Drizzle doesn't require a separate code generation step. Your schema files are regular TypeScript.
* **SQL-like syntax** — Queries read like SQL, which makes them easy to understand and debug.
* **Built-in migrations** — Drizzle Kit can generate and manage SQL migrations from your schema changes automatically.
* **Lightweight** — Drizzle has zero runtime dependencies and a small bundle size, which matters for mobile apps.

## Prerequisites[¶](#prerequisites "Permanent link")

Before you begin, make sure you have a Capacitor project with the [SQLite](/docs/plugins/sqlite/) plugin installed. To install the plugin, please refer to the [Installation](/docs/plugins/sqlite/#installation) section in the plugin documentation.

## Installation[¶](#installation "Permanent link")

Install the Drizzle adapter along with Drizzle ORM:

`[](#%5F%5Fcodelineno-0-1)npm install @capawesome/capacitor-sqlite-drizzle drizzle-orm
`

You'll also want to install Drizzle Kit as a dev dependency for schema migrations:

`[](#%5F%5Fcodelineno-1-1)npm install -D drizzle-kit
`

## Setting Up the Database[¶](#setting-up-the-database "Permanent link")

To get started, open a database using the [SQLite](/docs/plugins/sqlite/) plugin and pass it to the `drizzle()` function:

`[](#%5F%5Fcodelineno-2-1)import { Sqlite } from '@capawesome-team/capacitor-sqlite';
[](#%5F%5Fcodelineno-2-2)import { drizzle } from '@capawesome/capacitor-sqlite-drizzle';
[](#%5F%5Fcodelineno-2-3)
[](#%5F%5Fcodelineno-2-4)const { databaseId } = await Sqlite.open({ path: 'my.db' });
[](#%5F%5Fcodelineno-2-5)const db = drizzle(Sqlite, { databaseId });
`

The `drizzle()` function accepts the `Sqlite` plugin instance and a configuration object. The `databaseId` is the unique identifier returned by [open(...)](/docs/plugins/sqlite/#open) and is required to route queries to the correct database.

You can also pass additional Drizzle configuration options like `schema` and `logger`:

`[](#%5F%5Fcodelineno-3-1)import * as schema from './schema';
[](#%5F%5Fcodelineno-3-2)
[](#%5F%5Fcodelineno-3-3)const db = drizzle(Sqlite, { databaseId, schema, logger: true });
`

Passing `schema` enables Drizzle's relational query API (more on that later), and `logger: true` logs all executed SQL statements to the console — useful during development.

## Defining a Schema[¶](#defining-a-schema "Permanent link")

Drizzle uses a schema-as-code approach. You define your tables as TypeScript objects, which Drizzle uses for type inference and query building. Create a `schema.ts` file in your project:

`[](#%5F%5Fcodelineno-4-1)import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
[](#%5F%5Fcodelineno-4-2)
[](#%5F%5Fcodelineno-4-3)export const users = sqliteTable('users', {
[](#%5F%5Fcodelineno-4-4)  id: integer('id').primaryKey({ autoIncrement: true }),
[](#%5F%5Fcodelineno-4-5)  name: text('name').notNull(),
[](#%5F%5Fcodelineno-4-6)  email: text('email').notNull().unique(),
[](#%5F%5Fcodelineno-4-7)  createdAt: integer('created_at', { mode: 'timestamp' })
[](#%5F%5Fcodelineno-4-8)    .notNull()
[](#%5F%5Fcodelineno-4-9)    .$defaultFn(() => new Date()),
[](#%5F%5Fcodelineno-4-10)});
[](#%5F%5Fcodelineno-4-11)
[](#%5F%5Fcodelineno-4-12)export const posts = sqliteTable('posts', {
[](#%5F%5Fcodelineno-4-13)  id: integer('id').primaryKey({ autoIncrement: true }),
[](#%5F%5Fcodelineno-4-14)  title: text('title').notNull(),
[](#%5F%5Fcodelineno-4-15)  content: text('content'),
[](#%5F%5Fcodelineno-4-16)  authorId: integer('author_id')
[](#%5F%5Fcodelineno-4-17)    .notNull()
[](#%5F%5Fcodelineno-4-18)    .references(() => users.id),
[](#%5F%5Fcodelineno-4-19)});
`

A few things to note here:

* `sqliteTable` defines a table with its columns and constraints.
* Column types like `integer` and `text` map directly to SQLite types.
* The `mode: 'timestamp'` option on `createdAt` tells Drizzle to automatically convert between JavaScript `Date` objects and integer timestamps.
* `references(() => users.id)` creates a foreign key constraint linking `posts.authorId` to `users.id`.
* `$defaultFn` sets a default value at the application level, not in the database.

This schema definition serves as the single source of truth for both your TypeScript types and your database structure.

## Running Queries[¶](#running-queries "Permanent link")

With the schema in place, you can run type-safe CRUD operations. All queries return promises and use the familiar SQL patterns.

### Insert[¶](#insert "Permanent link")

`[](#%5F%5Fcodelineno-5-1)await db.insert(users).values({
[](#%5F%5Fcodelineno-5-2)  name: 'Alice',
[](#%5F%5Fcodelineno-5-3)  email: 'alice@example.com',
[](#%5F%5Fcodelineno-5-4)});
`

### Select[¶](#select "Permanent link")

`[](#%5F%5Fcodelineno-6-1)import { eq } from 'drizzle-orm';
[](#%5F%5Fcodelineno-6-2)
[](#%5F%5Fcodelineno-6-3)// Select all users
[](#%5F%5Fcodelineno-6-4)const allUsers = await db.select().from(users);
[](#%5F%5Fcodelineno-6-5)
[](#%5F%5Fcodelineno-6-6)// Select with a filter
[](#%5F%5Fcodelineno-6-7)const user = await db
[](#%5F%5Fcodelineno-6-8)  .select()
[](#%5F%5Fcodelineno-6-9)  .from(users)
[](#%5F%5Fcodelineno-6-10)  .where(eq(users.email, 'alice@example.com'));
`

### Update[¶](#update "Permanent link")

`[](#%5F%5Fcodelineno-7-1)await db
[](#%5F%5Fcodelineno-7-2)  .update(users)
[](#%5F%5Fcodelineno-7-3)  .set({ name: 'Bob' })
[](#%5F%5Fcodelineno-7-4)  .where(eq(users.id, 1));
`

### Delete[¶](#delete "Permanent link")

`[](#%5F%5Fcodelineno-8-1)await db.delete(users).where(eq(users.id, 1));
`

Every query is fully typed. The `allUsers` variable, for example, is automatically inferred as an array of objects matching the `users` table schema — no manual type annotations needed.

## Relational Queries[¶](#relational-queries "Permanent link")

When you pass a `schema` to the `drizzle()` function, you unlock Drizzle's relational query API. This lets you load related data in a single query without writing manual joins:

`[](#%5F%5Fcodelineno-9-1)const usersWithPosts = await db.query.users.findMany({
[](#%5F%5Fcodelineno-9-2)  with: { posts: true },
[](#%5F%5Fcodelineno-9-3)});
`

This returns all users along with their associated posts, based on the foreign key relationship defined in the schema. The result is fully typed and nested — each user object includes a `posts` array.

You can also use `findFirst` to retrieve a single record:

`[](#%5F%5Fcodelineno-10-1)const user = await db.query.users.findFirst({
[](#%5F%5Fcodelineno-10-2)  where: eq(users.id, 1),
[](#%5F%5Fcodelineno-10-3)  with: { posts: true },
[](#%5F%5Fcodelineno-10-4)});
`

## Transactions[¶](#transactions "Permanent link")

For operations that need to succeed or fail together, use transactions. Drizzle sends `BEGIN`, `COMMIT`, and `ROLLBACK` statements automatically:

`[](#%5F%5Fcodelineno-11-1)await db.transaction(async (tx) => {
[](#%5F%5Fcodelineno-11-2)  const [user] = await tx
[](#%5F%5Fcodelineno-11-3)    .insert(users)
[](#%5F%5Fcodelineno-11-4)    .values({ name: 'Alice', email: 'alice@example.com' })
[](#%5F%5Fcodelineno-11-5)    .returning();
[](#%5F%5Fcodelineno-11-6)  await tx
[](#%5F%5Fcodelineno-11-7)    .insert(posts)
[](#%5F%5Fcodelineno-11-8)    .values({ title: 'Hello World', content: '...', authorId: user.id });
[](#%5F%5Fcodelineno-11-9)});
`

If any statement inside the callback throws an error, the entire transaction is rolled back. This is essential for maintaining data integrity, especially when inserting related records across multiple tables.

## Migrations[¶](#migrations "Permanent link")

Manually managing database schema changes with raw SQL is tedious and error-prone. Drizzle Kit solves this by generating SQL migration files from your schema changes. The adapter provides a `migrate()` function to apply these migrations at runtime.

### 1\. Configure Drizzle Kit[¶](#1-configure-drizzle-kit "Permanent link")

Create a `drizzle.config.ts` file in your project root:

`[](#%5F%5Fcodelineno-12-1)import { defineConfig } from 'drizzle-kit';
[](#%5F%5Fcodelineno-12-2)
[](#%5F%5Fcodelineno-12-3)export default defineConfig({
[](#%5F%5Fcodelineno-12-4)  schema: './src/schema.ts',
[](#%5F%5Fcodelineno-12-5)  out: './src/drizzle',
[](#%5F%5Fcodelineno-12-6)  dialect: 'sqlite',
[](#%5F%5Fcodelineno-12-7)  driver: 'expo',
[](#%5F%5Fcodelineno-12-8)});
`

Note

The `driver: 'expo'` setting is required. It tells Drizzle Kit to generate a bundled `migrations.js` file that works in non-Node environments like Capacitor.

### 2\. Generate Migrations[¶](#2-generate-migrations "Permanent link")

Whenever you change your schema, run the following command to generate migration files:

`[](#%5F%5Fcodelineno-13-1)npx drizzle-kit generate
`

This creates SQL migration files and a `migrations.js` bundle in the output directory (e.g. `./src/drizzle/`).

Note

The generated `migrations.js` file imports `.sql` files as strings. Depending on your bundler, you may need a plugin to handle this. For Vite-based projects, use [vite-plugin-plain-text](https://www.npmjs.com/package/vite-plugin-plain-text). For Babel-based setups, use [babel-plugin-inline-import](https://www.npmjs.com/package/babel-plugin-inline-import).

### 3\. Apply Migrations[¶](#3-apply-migrations "Permanent link")

Import the generated migrations and apply them when your app starts:

`[](#%5F%5Fcodelineno-14-1)import { Sqlite } from '@capawesome-team/capacitor-sqlite';
[](#%5F%5Fcodelineno-14-2)import { drizzle, migrate } from '@capawesome/capacitor-sqlite-drizzle';
[](#%5F%5Fcodelineno-14-3)import migrations from './drizzle/migrations';
[](#%5F%5Fcodelineno-14-4)
[](#%5F%5Fcodelineno-14-5)const { databaseId } = await Sqlite.open({ path: 'my.db' });
[](#%5F%5Fcodelineno-14-6)const db = drizzle(Sqlite, { databaseId });
[](#%5F%5Fcodelineno-14-7)
[](#%5F%5Fcodelineno-14-8)await migrate(Sqlite, databaseId, migrations);
`

Each migration runs inside its own transaction. The adapter automatically creates a `__drizzle_migrations` table to track which migrations have already been applied, so calling `migrate()` multiple times is safe — only pending migrations are executed.

## Stay Updated[¶](#stay-updated "Permanent link")

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

[Subscribe to the Capawesome Newsletter](/newsletter/)

## Conclusion[¶](#conclusion "Permanent link")

With the `@capawesome/capacitor-sqlite-drizzle` adapter, you can use Drizzle ORM's type-safe queries, schema-as-code approach, and automated migrations in your Capacitor apps. The setup is straightforward: define your schema in TypeScript, generate migrations with Drizzle Kit, and run queries using a familiar SQL-like API — all without sacrificing type safety.

**Resources and related readings:**

* [Adapter on GitHub](https://github.com/capawesome-team/capacitor-sqlite-drivers/tree/main/packages/drizzle)
* [API Reference](/docs/plugins/sqlite/#api)
* [Exploring the Capacitor SQLite API](/blog/exploring-the-capacitor-sqlite-api/)
* For decorator-based ORMs, make sure to read [TypeORM](/blog/how-to-use-typeorm-with-capacitor-and-sqlite/)
* For a query-builder approach check [Kysely](/blog/how-to-use-kysely-with-capacitor-and-sqlite/)

Join the Capawesome [Discord](https://discord.gg/VCXxSVjefW) server for questions and subscribe to the Capawesome [newsletter](/newsletter/) to stay updated.

May 7, 2026 

 Back to top 