Become a Gold Sponsor

Get Started with Drizzle and Expo

This guide assumes familiarity with:
  • Expo SQLite - A library that provides access to a database that can be queried through a SQLite API - read here

Step 1 - Setup a project from Expo Template

npm
yarn
pnpm
bun
npx create expo-app --template blank-typescript

You can read more about this template here.

Basic file structure

After installing the template and adding the db folder, you’ll find the following content: In the db/schema.ts file with drizzle table definitions. The drizzle folder contains SQL migration files and snapshots

πŸ“¦ <project root>
 β”œ πŸ“‚ assets
 β”œ πŸ“‚ drizzle
 β”œ πŸ“‚ db
 β”‚  β”” πŸ“œ schema.ts
 β”œ πŸ“œ .gitignore
 β”œ πŸ“œ .npmrc
 β”œ πŸ“œ app.json
 β”œ πŸ“œ App.tsx
 β”œ πŸ“œ babel.config.ts
 β”œ πŸ“œ drizzle.config.ts
 β”œ πŸ“œ package.json
 β”” πŸ“œ tsconfig.json

Step 2 - Install expo-sqlite package

npm
yarn
pnpm
bun
npx expo install expo-sqlite

Step 3 - Install required packages

npm
yarn
pnpm
bun
npm i drizzle-orm
npm i -D drizzle-kit

Step 4 - Connect Drizzle ORM to the database

Create a App.tsx file in the root directory and initialize the connection:

import * as SQLite from 'expo-sqlite';
import { drizzle } from 'drizzle-orm/expo-sqlite';

const expo = SQLite.openDatabaseSync('db.db');

const db = drizzle(expo);

Step 4 - Create a table

Create a schema.ts file in the db directory and declare your table:

src/db/schema.ts
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const usersTable = sqliteTable("users_table", {
  id: int().primaryKey({ autoIncrement: true }),
  name: text().notNull(),
  age: int().notNull(),
  email: text().notNull().unique(),
});

Step 5 - Setup Drizzle config file

Drizzle config - a configuration file that is used by Drizzle Kit and contains all the information about your database connection, migration folder and schema files.

Create a drizzle.config.ts file in the root of your project and add the following content:

import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  dialect: 'sqlite',
  driver: 'expo',
  schema: './db/schema.ts',
  out: './drizzle',
});

Step 6 - Setup metro config

Create a file metro.config.js in root folder and add this code inside:

metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
config.resolver.sourceExts.push('sql');
module.exports = config;

Step 7 - Update babel config

metro.config.js
module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: [["inline-import", { "extensions": [".sql"] }]] // <-- add this
  };
};

Step 8 - Applying changes to the database

With Expo, you would need to generate migrations using the drizzle-kit generate command and then apply them at runtime using the drizzle-orm migrate() function

Generate migrations:

npx drizzle-kit generate

Step 9 - Apply migrations and query your db:

Let’s App.tsx file with migrations and queries to create, read, update, and delete users

import { Text, View } from 'react-native';
import * as SQLite from 'expo-sqlite';
import { useEffect, useState } from 'react';
import { drizzle } from 'drizzle-orm/expo-sqlite';
import { usersTable } from './db/schema';
import { useMigrations } from 'drizzle-orm/expo-sqlite/migrator';
import migrations from './drizzle/migrations';

const expo = SQLite.openDatabaseSync('db.db');

const db = drizzle(expo);

export default function App() {
  const { success, error } = useMigrations(db, migrations);
  const [items, setItems] = useState<typeof usersTable.$inferSelect[] | null>(null);

  useEffect(() => {
    if (!success) return;

    (async () => {
      await db.delete(usersTable);

      await db.insert(usersTable).values([
        {
            name: 'John',
            age: 30,
            email: '[email protected]',
        },
      ]);

      const users = await db.select().from(usersTable);
      setItems(users);
    })();
  }, [success]);

  if (error) {
    return (
      <View>
        <Text>Migration error: {error.message}</Text>
      </View>
    );
  }

  if (!success) {
    return (
      <View>
        <Text>Migration is in progress...</Text>
      </View>
    );
  }

  if (items === null || items.length === 0) {
    return (
      <View>
        <Text>Empty</Text>
      </View>
    );
  }

  return (
    <View
      style={{
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        width: '100%',
        height: '100%',
        justifyContent: 'center',
      }}
    >
      {items.map((item) => (
        <Text key={item.id}>{item.email}</Text>
      ))}
    </View>
  );
}

Step 10 - Prebuild and run expo app

npm
yarn
pnpm
bun
npx expo run:ios