rembrembdocs

Get Started with Drizzle and Expo

This guide assumes familiarity with:

Step 1 - Setup a project from Expo Template

npx create expo-app --template blank-typescript
yarn create expo-app --template blank-typescript
pnpm create expo-app --template blank-typescript
bunx 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

npx expo install expo-sqlite
yarn expo install expo-sqlite
pnpm expo install expo-sqlite
bunx expo install expo-sqlite

Step 3 - Install required packages

npm i drizzle-orm
npm i -D drizzle-kit
yarn add drizzle-orm
yarn add -D drizzle-kit
pnpm add drizzle-orm
pnpm add -D drizzle-kit
bun add drizzle-orm
bun add -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:

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:

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

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: 'john@example.com',
        },
      ]);

      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

npx expo run:ios
yarn expo run:ios
pnpm expo run:ios
bun expo run:ios