---
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
---

<!doctype html> 

[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/) 

* [ SDKs ](/docs/sdks/)
* [ Formbricks ](/docs/sdks/capacitor/formbricks/)
* [ Geocoder ](/docs/sdks/capacitor/geocoder/)
* [ Google Sign-In ](/docs/sdks/capacitor/google-sign-in/)
* [ Grafana Faro ](/docs/sdks/capacitor/grafana-faro/)
* [ libSQL ](/docs/sdks/capacitor/libsql/)
* [ Live Update ](/docs/sdks/capacitor/live-update/)
* [ Managed Configurations ](/docs/sdks/capacitor/managed-configurations/)
* [ Media Session ](/docs/sdks/capacitor/media-session/)
* [ ML Kit ](/docs/sdks/capacitor/mlkit/)
* [ Navigation Bar ](/docs/sdks/capacitor/navigation-bar/)
* [ NFC ](/docs/sdks/capacitor/nfc/)
* [ OAuth ](/docs/sdks/capacitor/oauth/)
* [ Pedometer ](/docs/sdks/capacitor/pedometer/)
* [ Photo Editor ](/docs/sdks/capacitor/photo-editor/)
* [ PostHog ](/docs/sdks/capacitor/posthog/)
* [ Printer ](/docs/sdks/capacitor/printer/)
* [ Purchases ](/docs/sdks/capacitor/purchases/)
* [ RealtimeKit ](/docs/sdks/capacitor/realtimekit/)
* [ Screen Orientation ](/docs/sdks/capacitor/screen-orientation/)
* [ Screenshot ](/docs/sdks/capacitor/screenshot/)
* [ Secure Preferences ](/docs/sdks/capacitor/secure-preferences/)
* [ Speech Recognition ](/docs/sdks/capacitor/speech-recognition/)
* [ Speech Synthesis ](/docs/sdks/capacitor/speech-synthesis/)
* [ Share Target ](/docs/sdks/capacitor/share-target/)
* [ Square Mobile Payments ](/docs/sdks/capacitor/square-mobile-payments/)
* [ SQLite ](/docs/sdks/capacitor/sqlite/)
* [ Superwall ](/docs/sdks/capacitor/superwall/)
* [ Torch ](/docs/sdks/capacitor/torch/)
* [ Vault ](/docs/sdks/capacitor/vault/)
* [ Wifi ](/docs/sdks/capacitor/wifi/)
* [ Zip ](/docs/sdks/capacitor/zip/)
* [ Cordova ](/docs/sdks/cordova/)
* [ Cloud ](/docs/cloud/)
* [ Integrations ](/docs/cloud/live-updates/integrations/)
* Concepts
* Reference
* [ Troubleshooting ](/docs/cloud/live-updates/troubleshooting/)
* [ FAQ ](/docs/cloud/live-updates/faq/)
* [ Native Builds ](/docs/cloud/native-builds/)
* [ Set Up Environments ](/docs/cloud/native-builds/environments/)
* [ Overwrite Native Configurations ](/docs/cloud/native-builds/native-configurations/)
* [ Auto-Increment Build Numbers ](/docs/cloud/native-builds/auto-incrementing-build-numbers/)
* [ Configure the Web Build Script ](/docs/cloud/native-builds/web-build-script/)
* [ Build from a Monorepo ](/docs/cloud/native-builds/monorepo/)
* [ Use pnpm, Yarn, or bun ](/docs/cloud/native-builds/package-managers/)
* [ Install Private npm Packages ](/docs/cloud/native-builds/npm-private-registry/)
* [ Override the Java Version ](/docs/cloud/native-builds/override-java-version/)
* [ Custom iOS Provisioning Profiles ](/docs/cloud/native-builds/custom-ios-provisioning-profiles/)
* [ Build without Git ](/docs/cloud/native-builds/build-without-git/)
* [ Access Git Behind a Firewall ](/docs/cloud/native-builds/firewall-access/)
* [ Integrations ](/docs/cloud/native-builds/integrations/)
* Reference
* [ Troubleshooting ](/docs/cloud/native-builds/troubleshooting/)
* [ FAQ ](/docs/cloud/native-builds/faq/)
* [ App Store Publishing ](/docs/cloud/app-store-publishing/)
* [ Submit a Build ](/docs/cloud/app-store-publishing/submit-a-build/)
* [ Submit Automatically After a Build ](/docs/cloud/app-store-publishing/submit-automatically/)
* [ Troubleshooting ](/docs/cloud/app-store-publishing/troubleshooting/)
* [ FAQ ](/docs/cloud/app-store-publishing/faq/)
* [ Automations ](/docs/cloud/automations/)
* [ Reference ](/docs/cloud/automations/reference/)
* [ Troubleshooting ](/docs/cloud/automations/troubleshooting/)
* [ FAQ ](/docs/cloud/automations/faq/)
* [ Assist ](/docs/cloud/assist/)
* [ CLI ](/docs/cloud/cli/)
* APIs and SDKs
* [ Webhooks ](/docs/cloud/webhooks/)
* [ Integrations ](/docs/cloud/integrations/)
* Account
* [ Organization ](/docs/cloud/organizations/)
* [ Two-Factor Enforcement ](/docs/cloud/organizations/two-factor-authentication/)
* [ Audit Logs ](/docs/cloud/organizations/audit-logs/)
* [ Billing ](/docs/cloud/organizations/billing/)
* [ License Keys ](/docs/cloud/license-keys/)
* [ AI ](/docs/ai/)
* [ Insiders ](/docs/insiders/)
* [ Billing & Plans ](/docs/insiders/billing-and-plans/)
* [ FAQ ](/docs/insiders/faq/)
* [ License ](https://capawesome.io/legal/eula/)
* [ Support ](/docs/support/)
* [ Contributing ](/docs/contributing/)
* Contributing code
* [ Code of Conduct ](/docs/contributing/code-of-conduct/)
* [ Questions ](https://docs.github.com/en/discussions/collaborating-with-your-community-using-discussions/participating-in-a-discussion#creating-a-discussion)
* [ 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/sdks/capacitor/sqlite/) using the new `@capawesome/capacitor-sqlite-drizzle` adapter. For the **Capacitor SQLite plugin** itself, see the [plugin documentation](/docs/sdks/capacitor/sqlite/#api).

[ ![Build and deploy your Capacitor app with Capawesome Cloud](https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1) ](/) 

## 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 [Capacitor SQLite plugin](/docs/sdks/capacitor/sqlite/) installed. To install the plugin, please refer to the [Installation](/docs/sdks/capacitor/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 [Capacitor SQLite plugin](/docs/sdks/capacitor/sqlite/) 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/sdks/capacitor/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/sdks/capacitor/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.

June 10, 2026 

Back to top

```json
{
      "@context": "https://schema.org",
      "@type": "BlogPosting",
      "headline": "How to Use Drizzle ORM with Capacitor and SQLite",
      "description": "Learn how to use Drizzle ORM with the Capacitor SQLite plugin to build type-safe database layers in your cross-platform mobile apps.",
      "image": "https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png",
      "datePublished": "2026-02-25T00:00:00+00:00",
      "dateModified": "2026-03-09T00:00:00+00:00",
      "author": [
        {
          "@type": "Person",
          "name": "Robin Genz",
          "url": "https://github.com/robingenz"
        }
      ],
      "publisher": {
        "@type": "Organization",
        "name": "Capawesome",
        "url": "https://capawesome.io",
        "logo": {
          "@type": "ImageObject",
          "url": "https://capawesome.io/assets/images/logo.svg"
        }
      },
      "articleSection": "Capacitor",
      "keywords": ["Capacitor", "Guides", "Plugins"],
      "mainEntityOfPage": "https://capawesome.io/blog/how-to-use-drizzle-orm-with-capacitor-and-sqlite/",
      "url": "https://capawesome.io/blog/how-to-use-drizzle-orm-with-capacitor-and-sqlite/"
    }
{
      "@context": "https://schema.org",
      "@type": "BreadcrumbList",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "name": "Home",
          "item": "https://capawesome.io/"
        },
        {
          "@type": "ListItem",
          "position": 2,
          "name": "Blog",
          "item": "https://capawesome.io/blog/"
        },
        {
          "@type": "ListItem",
          "position": 3,
          "name": "How to Use Drizzle ORM with Capacitor and SQLite",
          "item": "https://capawesome.io/blog/how-to-use-drizzle-orm-with-capacitor-and-sqlite/"
        }
      ]
    }
```
