Skip to content

Commit cb15889

Browse files
DaveyEkechrispadercodex
authored
feat: add Drizzle ORM driver (#448)
* feat: add executeRaw to return rows as arrays * feat: add drizzle driver package * feat: add drizzle migrator * test: run drizzle migrator on drizzle-kit output * fix: load drizzle type declarations in consuming projects * test: run drizzle driver on device * docs: add drizzle guide * fix(mock): refresh raw columns after schema changes Co-authored-by: Codex <noreply@openai.com> * fix: route global raw queries through the managed queue Co-authored-by: Codex <noreply@openai.com> * fix(drizzle): preserve blobs and serialize migrations Document the driver API and include it in the generated reference. Co-authored-by: Codex <noreply@openai.com> * fix(example): bundle shared Drizzle fixtures on macOS Include app and Metro output when integration tests time out. Co-authored-by: Codex <noreply@openai.com> * test: assert mixed queue submission order Check ordered writes after all operations finish instead of assuming native completion timing. Co-authored-by: Codex <noreply@openai.com> * ci: allow macOS integration tests to finish after cold builds Raise the job budget to 40 minutes so setup and compilation leave time for the test runner. Co-authored-by: Codex <noreply@openai.com> --------- Co-authored-by: Christoph Pader <chris@margelo.com> Co-authored-by: Codex <noreply@openai.com>
1 parent 076a2ba commit cb15889

47 files changed

Lines changed: 1708 additions & 50 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/ci-paths.json‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
"packages/react-native-nitro-sqlite-vec/**/*.jsx",
3434
"packages/react-native-nitro-sqlite-vec/tsconfig.json",
3535
"packages/react-native-nitro-sqlite-vec/package.json",
36+
"packages/react-native-nitro-sqlite-drizzle/**/*.ts",
37+
"packages/react-native-nitro-sqlite-drizzle/**/*.js",
38+
"packages/react-native-nitro-sqlite-drizzle/tsconfig.json",
39+
"packages/react-native-nitro-sqlite-drizzle/package.json",
3640
"scripts/**/*.ts",
3741
"example/jest*.js"
3842
],
@@ -75,6 +79,8 @@
7579
"packages/react-native-nitro-sqlite/package.json",
7680
"packages/react-native-nitro-sqlite-vec/src/**",
7781
"packages/react-native-nitro-sqlite-vec/package.json",
82+
"packages/react-native-nitro-sqlite-drizzle/src/**",
83+
"packages/react-native-nitro-sqlite-drizzle/package.json",
7884
"config/tsconfig.json",
7985
"package.json",
8086
"bun.lock"
@@ -110,9 +116,14 @@
110116
"scripts/android-sqlite-flags/**",
111117
"example/index.js",
112118
"example/babel.config*.js",
119+
"example/metro.config.js",
113120
"example/app.json",
114121
"packages/react-native-nitro-sqlite-vec/src/**",
115-
"!packages/react-native-nitro-sqlite-vec/src/__tests__/**"
122+
"!packages/react-native-nitro-sqlite-vec/src/__tests__/**",
123+
"packages/react-native-nitro-sqlite-drizzle/src/**",
124+
"packages/react-native-nitro-sqlite-drizzle/package.json",
125+
"!packages/react-native-nitro-sqlite-drizzle/src/__tests__/**",
126+
"packages/react-native-nitro-sqlite-drizzle/src/__tests__/fixtures/**"
116127
],
117128
"ios": [
118129
".github/workflows/**",
@@ -148,9 +159,14 @@
148159
"scripts/test-podspec-threadsafe.rb",
149160
"example/index.js",
150161
"example/babel.config*.js",
162+
"example/metro.config.js",
151163
"example/app.json",
152164
"packages/react-native-nitro-sqlite-vec/src/**",
153-
"!packages/react-native-nitro-sqlite-vec/src/__tests__/**"
165+
"!packages/react-native-nitro-sqlite-vec/src/__tests__/**",
166+
"packages/react-native-nitro-sqlite-drizzle/src/**",
167+
"packages/react-native-nitro-sqlite-drizzle/package.json",
168+
"!packages/react-native-nitro-sqlite-drizzle/src/__tests__/**",
169+
"packages/react-native-nitro-sqlite-drizzle/src/__tests__/fixtures/**"
154170
],
155171
"macos": [
156172
".github/workflows/**",
@@ -187,6 +203,10 @@
187203
"example/react-native.config.js",
188204
"example/macos/react-native.config.js",
189205
"!packages/react-native-nitro-sqlite/src/__tests__/**",
190-
"!packages/react-native-nitro-sqlite-vec/src/__tests__/**"
206+
"!packages/react-native-nitro-sqlite-vec/src/__tests__/**",
207+
"packages/react-native-nitro-sqlite-drizzle/src/**",
208+
"packages/react-native-nitro-sqlite-drizzle/package.json",
209+
"!packages/react-native-nitro-sqlite-drizzle/src/__tests__/**",
210+
"packages/react-native-nitro-sqlite-drizzle/src/__tests__/fixtures/**"
191211
]
192212
}

‎.github/workflows/ci.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,14 @@ jobs:
121121
bun --cwd example lint
122122
bun --cwd packages/react-native-nitro-sqlite lint
123123
bun --cwd packages/react-native-nitro-sqlite-vec lint
124+
bun --cwd packages/react-native-nitro-sqlite-drizzle lint
124125
git diff --exit-code HEAD -- . ':(exclude)bun.lock'
125126
- name: Test JavaScript wrappers and example runners
126127
if: steps.select.outputs.typescript == 'true'
127128
run: |
128129
(cd packages/react-native-nitro-sqlite && ../../node_modules/.bin/jest --coverage --runInBand)
129130
(cd packages/react-native-nitro-sqlite-vec && ../../node_modules/.bin/jest --coverage --runInBand)
131+
(cd packages/react-native-nitro-sqlite-drizzle && ../../node_modules/.bin/jest --coverage --runInBand)
130132
(cd example && ../node_modules/.bin/jest --config jest.mocha.config.js --runInBand --watchman=false)
131133
node --test example/macos/scripts/test-macos.test.mjs
132134
- name: Format C++

‎.github/workflows/test-macos.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
test:
1414
name: macOS integration tests
1515
runs-on: macos-26
16-
timeout-minutes: 30
16+
timeout-minutes: 40
1717
steps:
1818
- uses: actions/checkout@v7
1919
- uses: actions/download-artifact@v8

‎.prettierignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*.markdown
88

99
packages/react-native-nitro-sqlite/lib
10+
packages/react-native-nitro-sqlite-drizzle/src/__tests__/fixtures/drizzle
1011

1112
.well-known
1213
android

‎bun.lock‎

Lines changed: 155 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
title: Drizzle ORM
3+
description: Use Drizzle ORM with Nitro SQLite through the react-native-nitro-sqlite-drizzle driver.
4+
---
5+
6+
`react-native-nitro-sqlite-drizzle` runs [Drizzle ORM](https://orm.drizzle.team) queries on a Nitro SQLite connection. Pass an open connection to `drizzle()` and query it with Drizzle's API. Every query is asynchronous.
7+
8+
## Install
9+
10+
Install the driver with Drizzle and Nitro SQLite. Drizzle Kit and the Babel plugin are only needed for migrations.
11+
12+
```bash
13+
npm install react-native-nitro-sqlite react-native-nitro-sqlite-drizzle drizzle-orm
14+
npm install --save-dev drizzle-kit babel-plugin-inline-import
15+
```
16+
17+
The driver supports `drizzle-orm` 0.45.4 and later 0.x releases.
18+
19+
## Create a database
20+
21+
```ts
22+
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core'
23+
import { open } from 'react-native-nitro-sqlite'
24+
import { drizzle } from 'react-native-nitro-sqlite-drizzle'
25+
26+
export const users = sqliteTable('users', {
27+
id: integer('id').primaryKey(),
28+
name: text('name').notNull(),
29+
})
30+
31+
export const db = drizzle(open({ name: 'app.sqlite' }))
32+
33+
await db.insert(users).values({ id: 1, name: 'Ada' })
34+
const rows = await db.select().from(users)
35+
```
36+
37+
Create `db` once, outside your components. `drizzle(connection, config)` accepts Drizzle's `schema`, `logger`, and `casing` options. `db.$client` is the connection passed to `drizzle()`.
38+
39+
SQLite stores binary values as BLOBs. When you pass a `Buffer` or another typed-array view through Drizzle, the driver binds its bytes as an `ArrayBuffer`, preserving the view's byte range.
40+
41+
## Migrations
42+
43+
Drizzle Kit generates SQL migration files from your schema. Set `driver: 'expo'` so it also writes a `migrations.js` file that bundles them for React Native:
44+
45+
```ts title="drizzle.config.ts"
46+
import { defineConfig } from 'drizzle-kit'
47+
48+
export default defineConfig({
49+
schema: './db/schema.ts',
50+
out: './drizzle',
51+
dialect: 'sqlite',
52+
driver: 'expo',
53+
})
54+
```
55+
56+
Run `npx drizzle-kit generate` after each schema change.
57+
58+
`migrations.js` imports `.sql` files. Add the Babel plugin so they load as strings:
59+
60+
```js title="babel.config.js"
61+
module.exports = {
62+
presets: ['module:@react-native/babel-preset'],
63+
plugins: [['inline-import', { extensions: ['.sql'] }]],
64+
}
65+
```
66+
67+
Add `sql` to Metro's source extensions:
68+
69+
```js title="metro.config.js"
70+
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
71+
72+
const defaultConfig = getDefaultConfig(__dirname)
73+
74+
module.exports = mergeConfig(defaultConfig, {
75+
resolver: { sourceExts: [...defaultConfig.resolver.sourceExts, 'sql'] },
76+
})
77+
```
78+
79+
Run the migrations when the app starts with `useMigrations`. It applies the migrations this database has not run yet, in one transaction, and records them in the `__drizzle_migrations` table:
80+
81+
```tsx
82+
import { Text } from 'react-native'
83+
import { useMigrations } from 'react-native-nitro-sqlite-drizzle'
84+
import migrations from './drizzle/migrations'
85+
import { db } from './db'
86+
87+
export function App() {
88+
const { success, error } = useMigrations(db, migrations)
89+
90+
if (error) return <Text>Migration failed: {error.message}</Text>
91+
if (!success) return <Text>Migrating...</Text>
92+
return <Text>Ready</Text>
93+
}
94+
```
95+
96+
To run migrations outside a component, such as in startup code or a test, call `migrate` and await it:
97+
98+
```ts
99+
import { migrate } from 'react-native-nitro-sqlite-drizzle'
100+
101+
await migrate(db, migrations)
102+
```
103+
104+
Calls using the same connection wait for earlier migration calls before checking which migrations are pending. React Strict Mode can replay `useMigrations` without applying the same migration twice. If a migration fails, its transaction rolls back and a later call can retry it.
105+
106+
## Transactions
107+
108+
Run every query inside a transaction through `tx`:
109+
110+
```ts
111+
import { eq } from 'drizzle-orm'
112+
113+
await db.transaction(async (tx) => {
114+
await tx.insert(users).values({ id: 2, name: 'Grace' })
115+
await tx.update(users).set({ name: 'Ada L.' }).where(eq(users.id, 1))
116+
})
117+
```
118+
119+
A transaction holds the connection until its callback finishes. A query awaited through `db` inside the callback, including a query prepared from `db`, waits for the transaction and never completes. Nested `tx.transaction()` calls use savepoints.
120+
121+
`tx.rollback()` rolls back the transaction and rejects it with a `NitroSQLiteError` whose message is `Rollback`, not Drizzle's `TransactionRollbackError`. The `behavior` option of `db.transaction()` is ignored: transactions start with `BEGIN`. See [transactions](/docs/guides/transactions) for how Nitro SQLite runs them.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"title": "Integrations",
3-
"pages": ["typeorm", "vector-search"]
3+
"pages": ["drizzle", "typeorm", "vector-search"]
44
}

‎docs/scripts/check-api.mjs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ const requiredExports = {
4343
'createVectorTable',
4444
'knnSearch',
4545
],
46+
'react-native-nitro-sqlite-drizzle': [
47+
'drizzle',
48+
'migrate',
49+
'useMigrations',
50+
'MigrationConfig',
51+
'MigrationState',
52+
'NitroSQLiteDatabase',
53+
'NitroSQLiteSessionOptions',
54+
'NitroSQLiteSession',
55+
'NitroSQLiteTransaction',
56+
'NitroSQLitePreparedQuery',
57+
],
4658
}
4759

4860
for (const [packageName, exports] of Object.entries(requiredExports)) {

‎docs/tsconfig.typedoc.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"files": [
1313
"../packages/react-native-nitro-sqlite/src/index.ts",
1414
"../packages/react-native-nitro-sqlite/src/mock.ts",
15-
"../packages/react-native-nitro-sqlite-vec/src/index.ts"
15+
"../packages/react-native-nitro-sqlite-vec/src/index.ts",
16+
"../packages/react-native-nitro-sqlite-drizzle/src/index.ts"
1617
]
1718
}

‎docs/typedoc.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"entryPoints": [
33
"../packages/react-native-nitro-sqlite",
44
"../packages/react-native-nitro-sqlite-vec",
5+
"../packages/react-native-nitro-sqlite-drizzle",
56
"./mock-api"
67
],
78
"entryPointStrategy": "packages",

0 commit comments

Comments
 (0)