nanodb-orm 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +211 -378
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +270 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants/index.d.ts +7 -54
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +9 -61
- package/dist/constants/index.js.map +1 -1
- package/dist/core/config.d.ts +3 -13
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +5 -27
- package/dist/core/config.js.map +1 -1
- package/dist/core/connection.d.ts +10 -31
- package/dist/core/connection.d.ts.map +1 -1
- package/dist/core/connection.js +28 -78
- package/dist/core/connection.js.map +1 -1
- package/dist/core/index.d.ts +2 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -18
- package/dist/core/index.js.map +1 -1
- package/dist/index.d.ts +40 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +76 -31
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +79 -22
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +246 -48
- package/dist/init.js.map +1 -1
- package/dist/jest.setup.d.ts +4 -0
- package/dist/jest.setup.d.ts.map +1 -0
- package/dist/jest.setup.js +40 -0
- package/dist/jest.setup.js.map +1 -0
- package/dist/types/errors.d.ts +30 -12
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +98 -23
- package/dist/types/errors.js.map +1 -1
- package/dist/types/index.d.ts +130 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -4
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error-handler.d.ts +6 -31
- package/dist/utils/error-handler.d.ts.map +1 -1
- package/dist/utils/error-handler.js +24 -81
- package/dist/utils/error-handler.js.map +1 -1
- package/dist/utils/index.d.ts +1 -3
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -6
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logger.d.ts +6 -25
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +20 -38
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/migrations.d.ts +16 -90
- package/dist/utils/migrations.d.ts.map +1 -1
- package/dist/utils/migrations.js +216 -423
- package/dist/utils/migrations.js.map +1 -1
- package/dist/utils/schema-introspection.d.ts +30 -169
- package/dist/utils/schema-introspection.d.ts.map +1 -1
- package/dist/utils/schema-introspection.js +128 -462
- package/dist/utils/schema-introspection.js.map +1 -1
- package/dist/utils/seeds.d.ts +15 -48
- package/dist/utils/seeds.d.ts.map +1 -1
- package/dist/utils/seeds.js +101 -188
- package/dist/utils/seeds.js.map +1 -1
- package/dist/utils/sync.d.ts +16 -41
- package/dist/utils/sync.d.ts.map +1 -1
- package/dist/utils/sync.js +69 -172
- package/dist/utils/sync.js.map +1 -1
- package/dist/utils/transactions.d.ts +8 -47
- package/dist/utils/transactions.d.ts.map +1 -1
- package/dist/utils/transactions.js +32 -147
- package/dist/utils/transactions.js.map +1 -1
- package/package.json +29 -10
- package/dist/example.d.ts +0 -67
- package/dist/example.d.ts.map +0 -1
- package/dist/example.js +0 -86
- package/dist/example.js.map +0 -1
- package/dist/types/database.d.ts +0 -74
- package/dist/types/database.d.ts.map +0 -1
- package/dist/types/database.js +0 -6
- package/dist/types/database.js.map +0 -1
- package/dist/types/types.d.ts +0 -30
- package/dist/types/types.d.ts.map +0 -1
- package/dist/types/types.js +0 -6
- package/dist/types/types.js.map +0 -1
- package/llm.txt +0 -336
package/README.md
CHANGED
|
@@ -1,503 +1,336 @@
|
|
|
1
1
|
# nanodb-orm
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A lightweight, schema-agnostic ORM wrapper for Drizzle ORM with automatic migrations, schema introspection, CLI tools, and support for SQLite/Turso databases.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- ⚙️ **Configurable**: Flexible migration and seeding options
|
|
16
|
-
- 🧪 **Test Ready**: Built-in testing utilities and isolation
|
|
17
|
-
- 🔧 **Production Ready**: Enhanced error handling and reliability features
|
|
7
|
+
- **Auto-Migrations** — Automatically creates and updates database schemas from Drizzle tables
|
|
8
|
+
- **Schema Introspection** — Comprehensive schema analysis and validation
|
|
9
|
+
- **Multi-Database** — Works with local SQLite and remote Turso databases
|
|
10
|
+
- **Transactions** — Full transaction support with automatic rollback
|
|
11
|
+
- **CLI Tools** — Built-in commands including Drizzle Studio integration
|
|
12
|
+
- **Type Safe** — Full TypeScript support with proper type inference
|
|
13
|
+
- **Plugin System** — Extensible with hooks for audit, validation, transformations
|
|
14
|
+
- **Minimal** — ~1K lines of code, zero bloat
|
|
18
15
|
|
|
19
16
|
## Installation
|
|
20
17
|
|
|
21
18
|
```bash
|
|
22
19
|
npm install nanodb-orm
|
|
20
|
+
|
|
21
|
+
# For Drizzle Studio support (optional)
|
|
22
|
+
npm install drizzle-kit --save-dev
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## CLI
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
nanodb-orm includes a CLI for common database operations:
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
```bash
|
|
30
|
+
# Launch Drizzle Studio (visual database browser)
|
|
31
|
+
npx nanodb studio
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
33
|
+
# With custom port
|
|
34
|
+
npx nanodb studio --port 3000
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
name: text('name').notNull(),
|
|
38
|
-
age: integer('age').notNull(),
|
|
39
|
-
email: text('email').unique().notNull(),
|
|
40
|
-
});
|
|
36
|
+
# With specific database file
|
|
37
|
+
npx nanodb studio --db ./data/myapp.db
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
# Other commands
|
|
40
|
+
npx nanodb setup # Initialize schema and seed data
|
|
41
|
+
npx nanodb reset # Drop all tables and recreate
|
|
42
|
+
npx nanodb status # Show database health and stats
|
|
43
|
+
npx nanodb validate # Validate schema against database
|
|
44
|
+
npx nanodb help # Show all commands
|
|
44
45
|
```
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
// models/posts.ts
|
|
48
|
-
import { sql } from 'drizzle-orm';
|
|
49
|
-
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
50
|
-
import { usersTable } from './users';
|
|
47
|
+
### Drizzle Studio
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
54
|
-
title: text('title').notNull(),
|
|
55
|
-
content: text('content').notNull(),
|
|
56
|
-
userId: integer('user_id')
|
|
57
|
-
.notNull()
|
|
58
|
-
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
|
59
|
-
createdAt: text('created_at')
|
|
60
|
-
.default(sql`(datetime('now'))`)
|
|
61
|
-
.notNull(),
|
|
62
|
-
updatedAt: text('updated_at').$onUpdate(() => new Date().toISOString()),
|
|
63
|
-
});
|
|
49
|
+
Launch a visual database browser at `https://local.drizzle.studio`:
|
|
64
50
|
|
|
65
|
-
|
|
66
|
-
|
|
51
|
+
```bash
|
|
52
|
+
npx nanodb studio
|
|
67
53
|
```
|
|
68
54
|
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
### 1. Define Your Schema
|
|
60
|
+
|
|
69
61
|
```typescript
|
|
70
|
-
|
|
71
|
-
import { sql } from 'drizzle-orm';
|
|
72
|
-
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
62
|
+
import { table, integer, text } from 'nanodb-orm';
|
|
73
63
|
|
|
74
|
-
|
|
64
|
+
const users = table('users', {
|
|
75
65
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
76
66
|
name: text('name').notNull(),
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
isActive: integer('is_active').notNull().default(1),
|
|
80
|
-
createdAt: text('created_at')
|
|
81
|
-
.default(sql`(datetime('now'))`)
|
|
82
|
-
.notNull(),
|
|
67
|
+
email: text('email').unique().notNull(),
|
|
68
|
+
age: integer('age'),
|
|
83
69
|
});
|
|
84
70
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
const posts = table('posts', {
|
|
72
|
+
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
73
|
+
title: text('title').notNull(),
|
|
74
|
+
userId: integer('userId').notNull(),
|
|
75
|
+
});
|
|
88
76
|
|
|
89
|
-
|
|
90
|
-
// models/index.ts
|
|
91
|
-
export * from './users';
|
|
92
|
-
export * from './posts';
|
|
93
|
-
export * from './categories';
|
|
94
|
-
|
|
95
|
-
// Import tables for aggregation
|
|
96
|
-
import { usersTable } from './users';
|
|
97
|
-
import { postsTable } from './posts';
|
|
98
|
-
import { categoriesTable } from './categories';
|
|
99
|
-
|
|
100
|
-
// Export aggregated tables for nanodb-orm
|
|
101
|
-
export const tables = {
|
|
102
|
-
users: usersTable,
|
|
103
|
-
posts: postsTable,
|
|
104
|
-
categories: categoriesTable,
|
|
105
|
-
} as const;
|
|
77
|
+
const tables = { users, posts };
|
|
106
78
|
```
|
|
107
79
|
|
|
108
|
-
### 2.
|
|
80
|
+
### 2. Create Database
|
|
109
81
|
|
|
110
82
|
```typescript
|
|
111
|
-
import {
|
|
112
|
-
import { tables } from './models';
|
|
83
|
+
import { createDatabase } from 'nanodb-orm';
|
|
113
84
|
|
|
114
|
-
//
|
|
115
|
-
|
|
85
|
+
// One line: creates tables, runs migrations, seeds data, returns db
|
|
86
|
+
const db = await createDatabase({
|
|
116
87
|
tables,
|
|
117
88
|
seedData: {
|
|
118
|
-
users: [
|
|
119
|
-
|
|
120
|
-
{ name: 'Jane Smith', age: 25, email: 'jane@example.com' }
|
|
121
|
-
],
|
|
122
|
-
posts: [
|
|
123
|
-
{ title: 'Welcome Post', content: 'This is my first post!', userId: 1 },
|
|
124
|
-
{ title: 'Getting Started', content: 'Here are some tips...', userId: 2 }
|
|
125
|
-
],
|
|
126
|
-
categories: [
|
|
127
|
-
{ name: 'Technology', description: 'Tech-related posts', color: '#3B82F6' },
|
|
128
|
-
{ name: 'Lifestyle', description: 'Life and personal posts', color: '#10B981' }
|
|
129
|
-
]
|
|
130
|
-
}
|
|
89
|
+
users: [{ name: 'Alice', email: 'alice@example.com', age: 28 }],
|
|
90
|
+
},
|
|
131
91
|
});
|
|
132
92
|
|
|
133
|
-
//
|
|
134
|
-
|
|
93
|
+
// Store db and use it everywhere - no need for getInstance() or getDb()
|
|
94
|
+
export { db };
|
|
135
95
|
```
|
|
136
96
|
|
|
137
|
-
### 3.
|
|
138
|
-
|
|
139
|
-
nanodb-orm works seamlessly with Drizzle ORM table definitions. Here are the key Drizzle column types and methods:
|
|
97
|
+
### 3. Query with Type Safety
|
|
140
98
|
|
|
141
|
-
#### Drizzle Column Types
|
|
142
|
-
- `integer()` - Integer numbers
|
|
143
|
-
- `text()` - Text strings
|
|
144
|
-
- `real()` - Floating point numbers
|
|
145
|
-
- `blob()` - Binary data
|
|
146
|
-
|
|
147
|
-
#### Drizzle Column Methods
|
|
148
|
-
- `.primaryKey({ autoIncrement: true })` - Primary key with auto-increment
|
|
149
|
-
- `.notNull()` - NOT NULL constraint
|
|
150
|
-
- `.unique()` - UNIQUE constraint
|
|
151
|
-
- `.default(value)` - Default value
|
|
152
|
-
- `.references(table.column)` - Foreign key reference
|
|
153
|
-
|
|
154
|
-
#### Example Drizzle Column Definitions
|
|
155
99
|
```typescript
|
|
156
|
-
|
|
157
|
-
id: integer('id').primaryKey({ autoIncrement: true })
|
|
158
|
-
|
|
159
|
-
// Required text field
|
|
160
|
-
name: text('name').notNull()
|
|
100
|
+
import { eq, gte } from 'nanodb-orm';
|
|
161
101
|
|
|
162
|
-
//
|
|
163
|
-
|
|
102
|
+
// SELECT
|
|
103
|
+
const allUsers = await db.select().from(users);
|
|
104
|
+
const adults = await db.select().from(users).where(gte(users.age, 18));
|
|
164
105
|
|
|
165
|
-
//
|
|
166
|
-
|
|
106
|
+
// INSERT
|
|
107
|
+
await db.insert(users).values({ name: 'Bob', email: 'bob@example.com' });
|
|
167
108
|
|
|
168
|
-
//
|
|
169
|
-
|
|
109
|
+
// UPDATE
|
|
110
|
+
await db.update(users).set({ name: 'Robert' }).where(eq(users.email, 'bob@example.com'));
|
|
170
111
|
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
.default(sql`(datetime('now'))`)
|
|
174
|
-
.notNull()
|
|
175
|
-
|
|
176
|
-
// Foreign key reference
|
|
177
|
-
userId: integer('user_id')
|
|
178
|
-
.notNull()
|
|
179
|
-
.references(() => usersTable.id, { onDelete: 'cascade' })
|
|
112
|
+
// DELETE
|
|
113
|
+
await db.delete(users).where(eq(users.email, 'bob@example.com'));
|
|
180
114
|
```
|
|
181
115
|
|
|
182
116
|
## API Reference
|
|
183
117
|
|
|
184
|
-
### `
|
|
118
|
+
### `createDatabase(config)`
|
|
185
119
|
|
|
186
|
-
|
|
120
|
+
Create and initialize database. Returns `db` with all utilities attached.
|
|
187
121
|
|
|
188
122
|
```typescript
|
|
189
|
-
|
|
190
|
-
tables:
|
|
191
|
-
seedData
|
|
192
|
-
migrationConfig
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
123
|
+
const db = await createDatabase({
|
|
124
|
+
tables: { users, posts },
|
|
125
|
+
seedData: { users: [...] },
|
|
126
|
+
migrationConfig: {
|
|
127
|
+
preserveData: true, // default: true
|
|
128
|
+
autoMigrate: true, // default: true
|
|
129
|
+
dropTables: false, // default: false
|
|
130
|
+
},
|
|
131
|
+
plugins: [auditPlugin, validationPlugin], // optional
|
|
132
|
+
});
|
|
200
133
|
```
|
|
201
134
|
|
|
202
|
-
### `
|
|
203
|
-
|
|
204
|
-
Main database synchronization class.
|
|
135
|
+
### Database Operations (from `db`)
|
|
205
136
|
|
|
206
137
|
```typescript
|
|
207
|
-
//
|
|
208
|
-
await
|
|
209
|
-
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
//
|
|
214
|
-
|
|
138
|
+
// Health & Status
|
|
139
|
+
await db.healthCheck(); // { healthy, tables, totalRecords, ... }
|
|
140
|
+
await db.isReady(); // true/false
|
|
141
|
+
await db.sync(); // Sync with Turso (if remote)
|
|
142
|
+
|
|
143
|
+
// Reset & Seed
|
|
144
|
+
await db.reset(); // Drop all, recreate, reseed
|
|
145
|
+
await db.seed(); // Re-seed data
|
|
146
|
+
await db.clearData(); // Delete all data (keep tables)
|
|
215
147
|
```
|
|
216
148
|
|
|
217
|
-
### `
|
|
218
|
-
|
|
219
|
-
Comprehensive schema analysis utilities.
|
|
149
|
+
### Schema Introspection (from `db.schema`)
|
|
220
150
|
|
|
221
151
|
```typescript
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
|
|
152
|
+
db.schema.tables(); // ['users', 'posts']
|
|
153
|
+
db.schema.getTable('users'); // { columns, primaryKey, indexes }
|
|
154
|
+
db.schema.getColumns('users'); // ['id', 'name', 'email']
|
|
155
|
+
await db.schema.validate(); // { isValid, missingTables, ... }
|
|
156
|
+
db.schema.stats(); // Full schema statistics
|
|
157
|
+
db.schema.relationships(); // Foreign key relationships
|
|
158
|
+
```
|
|
227
159
|
|
|
228
|
-
|
|
229
|
-
const stats = SchemaIntrospection.getSchemaStats();
|
|
160
|
+
### Migrations (from `db.migrations`)
|
|
230
161
|
|
|
231
|
-
|
|
232
|
-
|
|
162
|
+
```typescript
|
|
163
|
+
await db.migrations.run(); // Run pending migrations
|
|
164
|
+
await db.migrations.validate(); // Validate schema vs DB
|
|
165
|
+
await db.migrations.checkTables(); // { users: true, posts: true }
|
|
233
166
|
```
|
|
234
167
|
|
|
235
|
-
### `
|
|
168
|
+
### `transaction(fn)`
|
|
236
169
|
|
|
237
|
-
|
|
170
|
+
Execute operations atomically.
|
|
238
171
|
|
|
239
172
|
```typescript
|
|
240
|
-
|
|
241
|
-
await DatabaseMigrations.initializeSchema();
|
|
173
|
+
import { transaction, sql } from 'nanodb-orm';
|
|
242
174
|
|
|
243
|
-
|
|
244
|
-
|
|
175
|
+
const result = await transaction(async (tx) => {
|
|
176
|
+
await tx.run(sql`INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')`);
|
|
177
|
+
await tx.run(sql`INSERT INTO posts (title, userId) VALUES ('Hello', 1)`);
|
|
178
|
+
return { created: true };
|
|
179
|
+
});
|
|
245
180
|
|
|
246
|
-
|
|
247
|
-
|
|
181
|
+
if (result.success) {
|
|
182
|
+
console.log(result.result);
|
|
183
|
+
} else {
|
|
184
|
+
console.log('Rolled back:', result.error?.message);
|
|
185
|
+
}
|
|
248
186
|
```
|
|
249
187
|
|
|
250
|
-
### `
|
|
188
|
+
### `parseDbError(error, context)`
|
|
251
189
|
|
|
252
|
-
|
|
190
|
+
Parse SQLite errors into user-friendly messages.
|
|
253
191
|
|
|
254
192
|
```typescript
|
|
255
|
-
|
|
256
|
-
await DatabaseSeeds.seedDatabase();
|
|
193
|
+
import { parseDbError } from 'nanodb-orm';
|
|
257
194
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
195
|
+
try {
|
|
196
|
+
await db.run('INSERT INTO users ...');
|
|
197
|
+
} catch (error) {
|
|
198
|
+
const parsed = parseDbError(error, { table: 'users', operation: 'insert' });
|
|
199
|
+
console.log(parsed.message); // "Column 'email' does not exist"
|
|
200
|
+
}
|
|
263
201
|
```
|
|
264
202
|
|
|
265
|
-
##
|
|
266
|
-
|
|
267
|
-
### Environment Variables
|
|
268
|
-
|
|
269
|
-
```bash
|
|
270
|
-
# Turso Database (optional)
|
|
271
|
-
TURSO_CONNECTION_URL=libsql://your-database.turso.io
|
|
272
|
-
TURSO_AUTH_TOKEN=your-auth-token
|
|
203
|
+
## Plugins
|
|
273
204
|
|
|
274
|
-
|
|
275
|
-
FORCE_LOCAL_DB=true
|
|
276
|
-
NODE_ENV=test
|
|
277
|
-
```
|
|
205
|
+
Extend nanodb-orm with custom hooks that run automatically on database operations.
|
|
278
206
|
|
|
279
|
-
###
|
|
207
|
+
### Plugin Interface
|
|
280
208
|
|
|
281
209
|
```typescript
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
210
|
+
import { NanoPlugin } from 'nanodb-orm';
|
|
211
|
+
|
|
212
|
+
const myPlugin: NanoPlugin = {
|
|
213
|
+
name: 'my-plugin',
|
|
214
|
+
|
|
215
|
+
// Lifecycle
|
|
216
|
+
install: (db) => db, // Modify db instance
|
|
217
|
+
onReady: (db) => {}, // Called after createDatabase
|
|
218
|
+
onError: (err, op, table) => {}, // Called on hook errors
|
|
219
|
+
|
|
220
|
+
// Auto hooks (run automatically)
|
|
221
|
+
beforeInsert: (table, data) => data, // Transform data before insert
|
|
222
|
+
afterInsert: (table, data, result) => {},
|
|
223
|
+
beforeUpdate: (table, data) => data, // Transform data before update
|
|
224
|
+
afterUpdate: (table, data, result) => {},
|
|
225
|
+
beforeDelete: (table, condition) => condition,
|
|
226
|
+
afterDelete: (table, condition, result) => {},
|
|
227
|
+
|
|
228
|
+
// Query hooks (also auto-triggered)
|
|
229
|
+
beforeQuery: (table, fields) => fields,
|
|
230
|
+
afterQuery: (table, fields, result) => {},
|
|
286
231
|
};
|
|
287
|
-
|
|
288
|
-
initializeDatabase({
|
|
289
|
-
tables,
|
|
290
|
-
migrationConfig
|
|
291
|
-
});
|
|
292
232
|
```
|
|
293
233
|
|
|
294
|
-
|
|
234
|
+
### Example Plugins
|
|
295
235
|
|
|
296
|
-
|
|
236
|
+
These are **example plugins you can create** - nanodb-orm provides the plugin system, you build the plugins:
|
|
297
237
|
|
|
298
238
|
```typescript
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
239
|
+
// Example: Audit logging with timing
|
|
240
|
+
const timers = new Map<string, number>();
|
|
241
|
+
const auditPlugin: NanoPlugin = {
|
|
242
|
+
name: 'audit',
|
|
243
|
+
beforeInsert: (table) => { timers.set('op', performance.now()); console.log(`INSERT ${table}`); },
|
|
244
|
+
afterInsert: () => { console.log(` ↳ ${(performance.now() - timers.get('op')!).toFixed(1)}ms`); },
|
|
245
|
+
beforeQuery: (table) => { timers.set('op', performance.now()); console.log(`SELECT ${table}`); },
|
|
246
|
+
afterQuery: (t, _, rows) => { console.log(` ↳ ${rows.length} rows in ${(performance.now() - timers.get('op')!).toFixed(1)}ms`); },
|
|
305
247
|
};
|
|
306
248
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
{ name: 'Alice', age: 25, email: 'alice@example.com' },
|
|
317
|
-
{ name: 'Bob', age: 30, email: 'bob@example.com' }
|
|
318
|
-
],
|
|
319
|
-
posts: [
|
|
320
|
-
{ title: 'Hello World', content: 'My first post', userId: 1 },
|
|
321
|
-
{ title: 'Second Post', content: 'Another post', userId: 2 }
|
|
322
|
-
]
|
|
249
|
+
// Auto-generate slugs
|
|
250
|
+
const slugPlugin: NanoPlugin = {
|
|
251
|
+
name: 'slug',
|
|
252
|
+
beforeInsert: (table, data) => {
|
|
253
|
+
if (table === 'posts' && data.title && !data.slug) {
|
|
254
|
+
return { ...data, slug: data.title.toLowerCase().replace(/\s+/g, '-') };
|
|
255
|
+
}
|
|
256
|
+
return data;
|
|
257
|
+
},
|
|
323
258
|
};
|
|
324
259
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
autoMigrate: true, // Enable auto-migrations
|
|
335
|
-
dropTables: true // Allow dropping tables
|
|
260
|
+
// Validation
|
|
261
|
+
const validationPlugin: NanoPlugin = {
|
|
262
|
+
name: 'validation',
|
|
263
|
+
beforeInsert: (table, data) => {
|
|
264
|
+
if (table === 'users' && !data.email?.includes('@')) {
|
|
265
|
+
throw new Error('Invalid email format');
|
|
266
|
+
}
|
|
267
|
+
return data;
|
|
268
|
+
},
|
|
336
269
|
};
|
|
337
270
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### Transaction Support (NEW in v0.0.3)
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
import { TransactionManager } from 'nanodb-orm';
|
|
348
|
-
|
|
349
|
-
// Atomic operations
|
|
350
|
-
await TransactionManager.execute(async (db) => {
|
|
351
|
-
await db.run('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
|
|
352
|
-
await db.run('INSERT INTO posts (title, content, userId) VALUES (?, ?, ?)', ['Hello', 'World', 1]);
|
|
353
|
-
// All operations succeed or all fail
|
|
271
|
+
// Use plugins
|
|
272
|
+
const db = await createDatabase({
|
|
273
|
+
tables,
|
|
274
|
+
plugins: [auditPlugin, slugPlugin, validationPlugin],
|
|
354
275
|
});
|
|
355
276
|
|
|
356
|
-
//
|
|
357
|
-
await
|
|
358
|
-
|
|
359
|
-
async (db) => await db.run('INSERT INTO posts ...'),
|
|
360
|
-
async (db) => await db.run('UPDATE stats ...')
|
|
361
|
-
]);
|
|
277
|
+
// Hooks run automatically
|
|
278
|
+
await db.insert(posts).values({ title: 'My Post' }); // slug auto-generated
|
|
279
|
+
await db.insert(users).values({ email: 'invalid' }); // throws error
|
|
362
280
|
|
|
363
|
-
//
|
|
364
|
-
|
|
365
|
-
await db.run('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
|
|
366
|
-
});
|
|
281
|
+
// Check loaded plugins
|
|
282
|
+
db.plugins.list(); // ['audit', 'slug', 'validation']
|
|
367
283
|
```
|
|
368
284
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
import { ErrorHandler, DatabaseError } from 'nanodb-orm';
|
|
373
|
-
|
|
374
|
-
try {
|
|
375
|
-
await DatabaseSync.setup();
|
|
376
|
-
} catch (error) {
|
|
377
|
-
if (error instanceof DatabaseError) {
|
|
378
|
-
console.log('Operation:', error.operation);
|
|
379
|
-
console.log('Context:', error.message);
|
|
380
|
-
console.log('Original Error:', error.originalError);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Non-throwing error handling
|
|
385
|
-
const result = ErrorHandler.handleNonThrowingError(
|
|
386
|
-
someError,
|
|
387
|
-
'optional-operation',
|
|
388
|
-
'This operation is optional'
|
|
389
|
-
);
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
### Thread-Safe Connections (NEW in v0.0.3)
|
|
393
|
-
|
|
394
|
-
```typescript
|
|
395
|
-
import { DatabaseConnection } from 'nanodb-orm';
|
|
396
|
-
|
|
397
|
-
// NEW: Async connection (recommended)
|
|
398
|
-
const db = await DatabaseConnection.getInstance();
|
|
399
|
-
|
|
400
|
-
// OLD: Synchronous connection (deprecated but still works)
|
|
401
|
-
const db = DatabaseConnection.getInstanceSync();
|
|
402
|
-
|
|
403
|
-
// Check connection status
|
|
404
|
-
if (DatabaseConnection.isConnected()) {
|
|
405
|
-
console.log('Database is connected');
|
|
406
|
-
}
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### Schema Introspection
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
import { SchemaIntrospection } from 'nanodb-orm';
|
|
413
|
-
|
|
414
|
-
// Get comprehensive schema information
|
|
415
|
-
const schemaInfo = SchemaIntrospection.getSchemaStats();
|
|
416
|
-
console.log('Total tables:', schemaInfo.totalTables);
|
|
417
|
-
console.log('Table details:', schemaInfo.tableDetails);
|
|
418
|
-
|
|
419
|
-
// Validate schema integrity
|
|
420
|
-
const validation = await SchemaIntrospection.validateSchema();
|
|
421
|
-
if (!validation.isValid) {
|
|
422
|
-
console.log('Schema issues:', validation.errors);
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
## Testing
|
|
427
|
-
|
|
428
|
-
The package includes built-in testing utilities:
|
|
285
|
+
## Configuration
|
|
429
286
|
|
|
430
|
-
|
|
431
|
-
import { DatabaseSync } from 'nanodb-orm';
|
|
287
|
+
### Environment Variables
|
|
432
288
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
});
|
|
289
|
+
```bash
|
|
290
|
+
# Remote Turso database
|
|
291
|
+
TURSO_CONNECTION_URL=libsql://your-db.turso.io
|
|
292
|
+
TURSO_AUTH_TOKEN=your-token
|
|
438
293
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
});
|
|
442
|
-
});
|
|
294
|
+
# Force local SQLite (for testing)
|
|
295
|
+
FORCE_LOCAL_DB=true
|
|
443
296
|
```
|
|
444
297
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
### Local SQLite
|
|
448
|
-
- Automatically used when Turso credentials are not available
|
|
449
|
-
- Perfect for development and testing
|
|
450
|
-
- File-based storage
|
|
298
|
+
### Database Selection
|
|
451
299
|
|
|
452
|
-
|
|
453
|
-
-
|
|
454
|
-
-
|
|
455
|
-
- Production-ready with global replication
|
|
300
|
+
- **Turso** — Used when `TURSO_CONNECTION_URL` and `TURSO_AUTH_TOKEN` are set
|
|
301
|
+
- **Local SQLite** — Fallback when Turso credentials are missing
|
|
302
|
+
- **Test Mode** — Isolated `test.db` when `NODE_ENV=test`
|
|
456
303
|
|
|
457
304
|
## Error Handling
|
|
458
305
|
|
|
459
|
-
|
|
306
|
+
Errors are automatically parsed into user-friendly messages:
|
|
460
307
|
|
|
461
308
|
```typescript
|
|
462
|
-
import { DatabaseError } from 'nanodb-orm';
|
|
309
|
+
import { DatabaseError, SchemaError, SeedError, parseDbError } from 'nanodb-orm';
|
|
463
310
|
|
|
464
311
|
try {
|
|
465
312
|
await DatabaseSync.setup();
|
|
466
313
|
} catch (error) {
|
|
467
314
|
if (error instanceof DatabaseError) {
|
|
468
|
-
console.log(
|
|
469
|
-
console.log('
|
|
315
|
+
console.log(error.message); // User-friendly message
|
|
316
|
+
console.log(error.operation); // 'seed', 'migration', etc.
|
|
317
|
+
console.log(error.table); // Table name if applicable
|
|
318
|
+
console.log(error.detail); // Additional context
|
|
470
319
|
}
|
|
471
320
|
}
|
|
472
321
|
```
|
|
473
322
|
|
|
474
|
-
|
|
323
|
+
Error output is clean and actionable:
|
|
475
324
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
325
|
+
```
|
|
326
|
+
┌─ nanodb-orm error ─────────────────────────────
|
|
327
|
+
│ Column "email" does not exist
|
|
328
|
+
│ Table: users
|
|
329
|
+
│ Operation: seed
|
|
330
|
+
│ Detail: Row data: {"name":"Alice","email":"alice@example.com"}
|
|
331
|
+
└────────────────────────────────────────────────
|
|
332
|
+
```
|
|
481
333
|
|
|
482
334
|
## License
|
|
483
335
|
|
|
484
336
|
MIT © Damilola Alao
|
|
485
|
-
|
|
486
|
-
## Changelog
|
|
487
|
-
|
|
488
|
-
### 0.0.2
|
|
489
|
-
- **Enhanced Documentation**: Added comprehensive Drizzle ORM integration examples
|
|
490
|
-
- **Real Model Examples**: Updated documentation with actual Drizzle table definitions
|
|
491
|
-
- **Schema Validation Fix**: Fixed table existence validation logic for proper health checks
|
|
492
|
-
- **LLM Documentation**: Added detailed `llm.txt` file for AI/LLM integration
|
|
493
|
-
- **Column Types Guide**: Added complete Drizzle column types and methods documentation
|
|
494
|
-
- **Foreign Key Support**: Documented foreign key relationships and cascade deletes
|
|
495
|
-
- **Type Safety**: Enhanced TypeScript integration examples with Drizzle's type inference
|
|
496
|
-
|
|
497
|
-
### 0.0.1
|
|
498
|
-
- Initial release
|
|
499
|
-
- Auto-migration system
|
|
500
|
-
- Schema introspection
|
|
501
|
-
- Multi-database support
|
|
502
|
-
- TypeScript support
|
|
503
|
-
- Testing utilities
|