hazo_connect 2.2.0 → 2.3.2

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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +563 -0
  3. package/dist/adapters/sqlite-adapter.d.ts.map +1 -1
  4. package/dist/adapters/sqlite-adapter.js +37 -2
  5. package/dist/adapters/sqlite-adapter.js.map +1 -1
  6. package/dist/factory.d.ts +1 -0
  7. package/dist/factory.d.ts.map +1 -1
  8. package/dist/factory.js +51 -9
  9. package/dist/factory.js.map +1 -1
  10. package/dist/nextjs/index.d.ts +2 -0
  11. package/dist/nextjs/index.d.ts.map +1 -1
  12. package/dist/nextjs/index.js +10 -1
  13. package/dist/nextjs/index.js.map +1 -1
  14. package/dist/nextjs/route-setup.d.ts +46 -0
  15. package/dist/nextjs/route-setup.d.ts.map +1 -0
  16. package/dist/nextjs/route-setup.js +141 -0
  17. package/dist/nextjs/route-setup.js.map +1 -0
  18. package/dist/nextjs/setup-helpers.d.ts +86 -0
  19. package/dist/nextjs/setup-helpers.d.ts.map +1 -0
  20. package/dist/nextjs/setup-helpers.js +174 -0
  21. package/dist/nextjs/setup-helpers.js.map +1 -0
  22. package/dist/server/index.d.ts +2 -1
  23. package/dist/server/index.d.ts.map +1 -1
  24. package/dist/server/index.js +7 -1
  25. package/dist/server/index.js.map +1 -1
  26. package/dist/sqlite/admin-service.d.ts +8 -0
  27. package/dist/sqlite/admin-service.d.ts.map +1 -1
  28. package/dist/sqlite/admin-service.js +39 -1
  29. package/dist/sqlite/admin-service.js.map +1 -1
  30. package/dist/utils/config-validator.d.ts +39 -0
  31. package/dist/utils/config-validator.d.ts.map +1 -0
  32. package/dist/utils/config-validator.js +78 -0
  33. package/dist/utils/config-validator.js.map +1 -0
  34. package/docs/examples/nextjs-admin-ui-setup.ts +199 -0
  35. package/docs/examples/nextjs-api-route.ts +205 -0
  36. package/docs/examples/nextjs-crud-service.ts +257 -0
  37. package/docs/examples/nextjs-server-component.tsx +123 -0
  38. package/docs/examples/nextjs-singleton-pattern.ts +166 -0
  39. package/docs/migration-guide.md +272 -0
  40. package/docs/nextjs-setup.md +471 -0
  41. package/docs/techdoc.md +824 -0
  42. package/docs/troubleshooting.md +442 -0
  43. package/docs/types.md +490 -0
  44. package/package.json +16 -4
  45. package/scripts/postinstall-setup.js +72 -0
  46. package/scripts/setup-routes.js +123 -0
@@ -0,0 +1,471 @@
1
+ # Next.js Setup Guide for hazo_connect
2
+
3
+ This guide provides complete setup instructions for using `hazo_connect` with SQLite in Next.js projects.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Quick Start](#quick-start)
8
+ - [Next.js Configuration](#nextjs-configuration)
9
+ - [Environment Variables](#environment-variables)
10
+ - [Setup Patterns](#setup-patterns)
11
+ - [Singleton Pattern](#singleton-pattern)
12
+ - [Common Patterns](#common-patterns)
13
+ - [Admin UI Setup](#admin-ui-setup)
14
+
15
+ ## Quick Start
16
+
17
+ 1. **Install hazo_connect:**
18
+ ```bash
19
+ npm install hazo_connect
20
+ ```
21
+
22
+ 2. **Configure Next.js** (see [Next.js Configuration](#nextjs-configuration))
23
+
24
+ 3. **Set environment variables** (see [Environment Variables](#environment-variables))
25
+
26
+ 4. **Create adapter instance** in your API routes:
27
+ ```typescript
28
+ import { createHazoConnect } from 'hazo_connect/server'
29
+ import { QueryBuilder } from 'hazo_connect/server'
30
+
31
+ const hazo = createHazoConnect({
32
+ type: 'sqlite',
33
+ sqlite: {
34
+ database_path: process.env.HAZO_CONNECT_SQLITE_PATH || './database.sqlite'
35
+ }
36
+ })
37
+ ```
38
+
39
+ ## Next.js Configuration
40
+
41
+ ### Required Configuration
42
+
43
+ Add the following to your `next.config.mjs`:
44
+
45
+ ```javascript
46
+ import path from 'path'
47
+
48
+ /** @type {import('next').NextConfig} */
49
+ const next_config = {
50
+ reactStrictMode: true,
51
+
52
+ // CRITICAL: Exclude sql.js from server component bundling
53
+ // sql.js uses Node.js module.exports which doesn't work in webpack context
54
+ serverComponentsExternalPackages: [
55
+ "sql.js",
56
+ "better-sqlite3",
57
+ ],
58
+
59
+ // CRITICAL: Exclude sql.js from webpack bundling for API routes
60
+ webpack: (config, { isServer }) => {
61
+ if (isServer) {
62
+ config.externals = config.externals || []
63
+ if (Array.isArray(config.externals)) {
64
+ config.externals.push("sql.js")
65
+ } else {
66
+ config.externals = [config.externals, "sql.js"]
67
+ }
68
+ }
69
+
70
+ // Enable WebAssembly support for sql.js
71
+ config.experiments = {
72
+ ...(config.experiments ?? {}),
73
+ asyncWebAssembly: true,
74
+ }
75
+
76
+ return config
77
+ },
78
+
79
+ // Optional: Set environment variables for SQLite configuration
80
+ env: {
81
+ HAZO_CONNECT_ENABLE_ADMIN_UI: process.env.HAZO_CONNECT_ENABLE_ADMIN_UI || "false",
82
+ HAZO_CONNECT_SQLITE_PATH: process.env.HAZO_CONNECT_SQLITE_PATH
83
+ ? path.resolve(process.env.HAZO_CONNECT_SQLITE_PATH)
84
+ : path.resolve(process.cwd(), "database.sqlite"),
85
+ },
86
+ }
87
+
88
+ export default next_config
89
+ ```
90
+
91
+ ### Why This Configuration is Needed
92
+
93
+ 1. **`serverComponentsExternalPackages`**: Prevents Next.js from bundling `sql.js` for Server Components. SQLite uses Node.js `module.exports`, which webpack cannot handle.
94
+
95
+ 2. **`webpack.externals`**: Excludes `sql.js` from webpack bundling for API routes, allowing it to run in the Node.js runtime where it works correctly.
96
+
97
+ 3. **`asyncWebAssembly: true`**: Enables WebAssembly support required by `sql.js` for database operations.
98
+
99
+ ### Configuration Options Explained
100
+
101
+ | Option | Purpose | Required |
102
+ |--------|---------|----------|
103
+ | `serverComponentsExternalPackages` | Prevents bundling of Node.js-only packages | Yes |
104
+ | `webpack.externals` | Excludes packages from webpack bundling | Yes |
105
+ | `asyncWebAssembly` | Enables WASM support for sql.js | Yes |
106
+ | `env` | Makes environment variables available at build time | Optional |
107
+
108
+ ## Environment Variables
109
+
110
+ ### SQLite Configuration
111
+
112
+ | Variable | Required | Default | Description |
113
+ |----------|----------|---------|-------------|
114
+ | `HAZO_CONNECT_TYPE` | No | `"sqlite"` | Database type: `"sqlite"` or `"postgrest"` |
115
+ | `HAZO_CONNECT_SQLITE_PATH` | No* | `"./database.sqlite"` | Path to SQLite database file (absolute or relative to `process.cwd()`) |
116
+ | `HAZO_CONNECT_SQLITE_READONLY` | No | `"false"` | Enable read-only mode (`"true"` or `"false"`) |
117
+ | `HAZO_CONNECT_SQLITE_WASM_DIR` | No | Auto-detected | Directory containing `sql-wasm.wasm` file |
118
+ | `HAZO_CONNECT_ENABLE_ADMIN_UI` | No | `"false"` | Enable SQLite admin UI (`"true"` or `"false"`) |
119
+
120
+ \* Required if `HAZO_CONNECT_TYPE=sqlite` and no fallback path is provided
121
+
122
+ ### PostgREST Configuration
123
+
124
+ | Variable | Required | Default | Description |
125
+ |----------|----------|---------|-------------|
126
+ | `POSTGREST_URL` | Yes* | - | PostgREST API base URL |
127
+ | `POSTGREST_API_KEY` | Yes* | - | PostgREST API key |
128
+
129
+ \* Required if using PostgREST adapter
130
+
131
+ ### Example `.env.local`
132
+
133
+ ```bash
134
+ # SQLite Configuration
135
+ HAZO_CONNECT_TYPE=sqlite
136
+ HAZO_CONNECT_SQLITE_PATH=./data/database.sqlite
137
+ HAZO_CONNECT_SQLITE_READONLY=false
138
+ HAZO_CONNECT_ENABLE_ADMIN_UI=true
139
+
140
+ # Optional: Custom WASM directory
141
+ # HAZO_CONNECT_SQLITE_WASM_DIR=./public
142
+ ```
143
+
144
+ ## Setup Patterns
145
+
146
+ ### Pattern 1: Direct Configuration (Recommended for Simple Cases)
147
+
148
+ ```typescript
149
+ // app/api/users/route.ts
150
+ import { createHazoConnect } from 'hazo_connect/server'
151
+ import { QueryBuilder } from 'hazo_connect/server'
152
+ import { NextResponse } from 'next/server'
153
+ import path from 'path'
154
+
155
+ export async function GET() {
156
+ const hazo = createHazoConnect({
157
+ type: 'sqlite',
158
+ sqlite: {
159
+ database_path: process.env.HAZO_CONNECT_SQLITE_PATH
160
+ ? path.resolve(process.env.HAZO_CONNECT_SQLITE_PATH)
161
+ : path.resolve(process.cwd(), 'database.sqlite')
162
+ }
163
+ })
164
+
165
+ const users = await hazo.query(
166
+ new QueryBuilder().from('users').select('*')
167
+ )
168
+
169
+ return NextResponse.json({ data: users })
170
+ }
171
+ ```
172
+
173
+ ### Pattern 2: Using Setup Helper (Recommended)
174
+
175
+ ```typescript
176
+ // app/api/users/route.ts
177
+ import { createHazoConnectFromEnv } from 'hazo_connect/nextjs/setup'
178
+ import { QueryBuilder } from 'hazo_connect/server'
179
+ import { NextResponse } from 'next/server'
180
+
181
+ export async function GET() {
182
+ const hazo = createHazoConnectFromEnv()
183
+
184
+ const users = await hazo.query(
185
+ new QueryBuilder().from('users').select('*')
186
+ )
187
+
188
+ return NextResponse.json({ data: users })
189
+ }
190
+ ```
191
+
192
+ ## Singleton Pattern
193
+
194
+ For multiple API routes that need database access, use a singleton pattern to reuse a single adapter instance:
195
+
196
+ ### Using Singleton Helper
197
+
198
+ ```typescript
199
+ // lib/hazo_connect.ts
200
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup'
201
+
202
+ export const hazo = getHazoConnectSingleton()
203
+ ```
204
+
205
+ ```typescript
206
+ // app/api/users/route.ts
207
+ import { hazo } from '@/lib/hazo_connect'
208
+ import { QueryBuilder } from 'hazo_connect/server'
209
+ import { NextResponse } from 'next/server'
210
+
211
+ export async function GET() {
212
+ const users = await hazo.query(
213
+ new QueryBuilder().from('users').select('*')
214
+ )
215
+
216
+ return NextResponse.json({ data: users })
217
+ }
218
+ ```
219
+
220
+ ```typescript
221
+ // app/api/posts/route.ts
222
+ import { hazo } from '@/lib/hazo_connect'
223
+ import { QueryBuilder } from 'hazo_connect/server'
224
+ import { NextResponse } from 'next/server'
225
+
226
+ export async function GET() {
227
+ const posts = await hazo.query(
228
+ new QueryBuilder().from('posts').select('*')
229
+ )
230
+
231
+ return NextResponse.json({ data: posts })
232
+ }
233
+ ```
234
+
235
+ ### Manual Singleton Pattern
236
+
237
+ If you prefer to create your own singleton:
238
+
239
+ ```typescript
240
+ // lib/hazo_connect.ts
241
+ import { createHazoConnect } from 'hazo_connect/server'
242
+ import type { HazoConnectAdapter } from 'hazo_connect/server'
243
+ import path from 'path'
244
+
245
+ let hazoInstance: HazoConnectAdapter | null = null
246
+
247
+ export function getHazoConnect(): HazoConnectAdapter {
248
+ if (!hazoInstance) {
249
+ hazoInstance = createHazoConnect({
250
+ type: 'sqlite',
251
+ sqlite: {
252
+ database_path: process.env.HAZO_CONNECT_SQLITE_PATH
253
+ ? path.resolve(process.env.HAZO_CONNECT_SQLITE_PATH)
254
+ : path.resolve(process.cwd(), 'database.sqlite'),
255
+ read_only: process.env.HAZO_CONNECT_SQLITE_READONLY === 'true'
256
+ },
257
+ enable_admin_ui: process.env.HAZO_CONNECT_ENABLE_ADMIN_UI === 'true'
258
+ })
259
+ }
260
+
261
+ return hazoInstance
262
+ }
263
+ ```
264
+
265
+ **Benefits of Singleton Pattern:**
266
+ - Single database connection shared across all API routes
267
+ - Reduced memory usage
268
+ - Faster response times (no connection overhead per request)
269
+ - Thread-safe in Next.js serverless environment
270
+
271
+ ## Common Patterns
272
+
273
+ ### API Route Pattern (Recommended)
274
+
275
+ ```typescript
276
+ // app/api/users/route.ts
277
+ import { createHazoConnect } from 'hazo_connect/server'
278
+ import { QueryBuilder } from 'hazo_connect/server'
279
+ import { NextResponse } from 'next/server'
280
+
281
+ export async function GET() {
282
+ const hazo = createHazoConnect({
283
+ type: 'sqlite',
284
+ sqlite: {
285
+ database_path: process.env.HAZO_CONNECT_SQLITE_PATH || './database.sqlite'
286
+ }
287
+ })
288
+
289
+ try {
290
+ const users = await hazo.query(
291
+ new QueryBuilder().from('users').select('*')
292
+ )
293
+ return NextResponse.json({ data: users })
294
+ } catch (error) {
295
+ return NextResponse.json(
296
+ { error: error instanceof Error ? error.message : 'Unknown error' },
297
+ { status: 500 }
298
+ )
299
+ }
300
+ }
301
+
302
+ // Update a user by ID
303
+ // Important: Updates use 'PATCH' method, not a method called 'update'
304
+ export async function PATCH(request: Request) {
305
+ const hazo = createHazoConnect({
306
+ type: 'sqlite',
307
+ sqlite: {
308
+ database_path: process.env.HAZO_CONNECT_SQLITE_PATH || './database.sqlite'
309
+ }
310
+ })
311
+
312
+ try {
313
+ const { id, ...updates } = await request.json()
314
+
315
+ // Use QueryBuilder with PATCH method and .where() to specify which row to update
316
+ const updated = await hazo.query(
317
+ new QueryBuilder()
318
+ .from('users')
319
+ .where('id', 'eq', id),
320
+ 'PATCH',
321
+ updates
322
+ )
323
+
324
+ return NextResponse.json({ data: updated })
325
+ } catch (error) {
326
+ return NextResponse.json(
327
+ { error: error instanceof Error ? error.message : 'Unknown error' },
328
+ { status: 500 }
329
+ )
330
+ }
331
+ }
332
+
333
+ // Delete a user by ID
334
+ // Important: Deletes use 'DELETE' method, not a method called 'delete'
335
+ export async function DELETE(request: Request) {
336
+ const hazo = createHazoConnect({
337
+ type: 'sqlite',
338
+ sqlite: {
339
+ database_path: process.env.HAZO_CONNECT_SQLITE_PATH || './database.sqlite'
340
+ }
341
+ })
342
+
343
+ try {
344
+ const { id } = await request.json()
345
+
346
+ if (!id) {
347
+ return NextResponse.json(
348
+ { error: 'ID is required' },
349
+ { status: 400 }
350
+ )
351
+ }
352
+
353
+ // Use QueryBuilder with DELETE method and .where() to specify which row to delete
354
+ await hazo.query(
355
+ new QueryBuilder()
356
+ .from('users')
357
+ .where('id', 'eq', id),
358
+ 'DELETE'
359
+ )
360
+
361
+ return NextResponse.json({ success: true })
362
+ } catch (error) {
363
+ return NextResponse.json(
364
+ { error: error instanceof Error ? error.message : 'Unknown error' },
365
+ { status: 500 }
366
+ )
367
+ }
368
+ }
369
+ ```
370
+
371
+ ### Server Component Pattern (Fetch from API Route)
372
+
373
+ ```typescript
374
+ // app/users/page.tsx
375
+ import { headers } from 'next/headers'
376
+
377
+ export default async function UsersPage() {
378
+ const headersList = await headers()
379
+ const host = headersList.get('host') || 'localhost:3000'
380
+ const baseUrl = `http://${host}`
381
+
382
+ const response = await fetch(`${baseUrl}/api/users`, {
383
+ cache: 'no-store'
384
+ })
385
+
386
+ const { data } = await response.json()
387
+
388
+ return (
389
+ <div>
390
+ <h1>Users</h1>
391
+ <ul>
392
+ {data.map((user: any) => (
393
+ <li key={user.id}>{user.name}</li>
394
+ ))}
395
+ </ul>
396
+ </div>
397
+ )
398
+ }
399
+ ```
400
+
401
+ ### Client Component Pattern (Fetch from API Route)
402
+
403
+ ```typescript
404
+ // app/users/client-page.tsx
405
+ 'use client'
406
+
407
+ import { useEffect, useState } from 'react'
408
+
409
+ export default function UsersClientPage() {
410
+ const [users, setUsers] = useState([])
411
+
412
+ useEffect(() => {
413
+ fetch('/api/users')
414
+ .then(res => res.json())
415
+ .then(data => setUsers(data.data))
416
+ }, [])
417
+
418
+ return (
419
+ <div>
420
+ <h1>Users</h1>
421
+ <ul>
422
+ {users.map((user: any) => (
423
+ <li key={user.id}>{user.name}</li>
424
+ ))}
425
+ </ul>
426
+ </div>
427
+ )
428
+ }
429
+ ```
430
+
431
+ ## Admin UI Setup
432
+
433
+ ### Enable Admin UI
434
+
435
+ 1. **Set environment variable:**
436
+ ```bash
437
+ HAZO_CONNECT_ENABLE_ADMIN_UI=true
438
+ ```
439
+
440
+ 2. **Initialize in your adapter creation:**
441
+ ```typescript
442
+ const hazo = createHazoConnect({
443
+ type: 'sqlite',
444
+ enable_admin_ui: true,
445
+ sqlite: {
446
+ database_path: process.env.HAZO_CONNECT_SQLITE_PATH || './database.sqlite'
447
+ }
448
+ })
449
+ ```
450
+
451
+ 3. **Access the admin UI:**
452
+ - Navigate to `/hazo_connect/sqlite_admin` in your browser
453
+ - The UI is automatically included when you install `hazo_connect`
454
+
455
+ ### Admin UI API Routes
456
+
457
+ The admin UI uses these API routes (automatically included):
458
+
459
+ - `GET /hazo_connect/api/sqlite/tables` - List all tables
460
+ - `GET /hazo_connect/api/sqlite/schema?table=TABLE_NAME` - Get table schema
461
+ - `GET /hazo_connect/api/sqlite/data?table=TABLE_NAME` - Get table data
462
+ - `POST /hazo_connect/api/sqlite/data` - Insert row
463
+ - `PATCH /hazo_connect/api/sqlite/data` - Update rows (Note: Uses `PATCH` method, not a method called `update`)
464
+ - `DELETE /hazo_connect/api/sqlite/data` - Delete rows (Note: Uses `DELETE` method, not a method called `delete`)
465
+
466
+ ## Next Steps
467
+
468
+ - See [Troubleshooting Guide](./troubleshooting.md) for common issues
469
+ - See [Code Examples](./examples/) for complete working examples
470
+ - See [Migration Guide](./migration-guide.md) for migrating existing code
471
+