drizzle-multitenant 1.0.8 → 1.0.9
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/LICENSE +1 -1
- package/README.md +36 -372
- package/dist/cli/index.js +686 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/{context-DBerWr50.d.ts → context-DoHx79MS.d.ts} +1 -1
- package/dist/cross-schema/index.d.ts +152 -1
- package/dist/cross-schema/index.js +208 -1
- package/dist/cross-schema/index.js.map +1 -1
- package/dist/index.d.ts +62 -5
- package/dist/index.js +1181 -50
- package/dist/index.js.map +1 -1
- package/dist/integrations/express.d.ts +3 -3
- package/dist/integrations/fastify.d.ts +3 -3
- package/dist/integrations/nestjs/index.d.ts +1 -1
- package/dist/integrations/nestjs/index.js +484 -3
- package/dist/integrations/nestjs/index.js.map +1 -1
- package/dist/migrator/index.d.ts +116 -1
- package/dist/migrator/index.js +418 -0
- package/dist/migrator/index.js.map +1 -1
- package/dist/types-B5eSRLFW.d.ts +235 -0
- package/package.json +9 -3
- package/dist/types-DKVaTaIb.d.ts +0 -130
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,30 +1,20 @@
|
|
|
1
1
|
# drizzle-multitenant
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/drizzle-multitenant)
|
|
4
|
-
[](https://github.com/mateusflorez/drizzle-multitenant)
|
|
5
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://mateusflorez.github.io/drizzle-multitenant/)
|
|
6
6
|
|
|
7
7
|
Multi-tenancy toolkit for Drizzle ORM with schema isolation, tenant context, and parallel migrations.
|
|
8
8
|
|
|
9
|
-
```
|
|
10
|
-
┌─────────────────────────────────────────────────────────────────────┐
|
|
11
|
-
│ ╔═══════════════════════════════════════════════════════════════╗ │
|
|
12
|
-
│ ║ drizzle-multitenant ║ │
|
|
13
|
-
│ ║ ━━━━━━━━━━━━━━━━━━━━ ║ │
|
|
14
|
-
│ ║ ║ │
|
|
15
|
-
│ ║ Schema isolation • Context propagation • Migrations ║ │
|
|
16
|
-
│ ╚═══════════════════════════════════════════════════════════════╝ │
|
|
17
|
-
└─────────────────────────────────────────────────────────────────────┘
|
|
18
|
-
```
|
|
19
|
-
|
|
20
9
|
## Features
|
|
21
10
|
|
|
22
|
-
- **Schema Isolation** -
|
|
23
|
-
- **Context Propagation** - AsyncLocalStorage-based tenant context
|
|
24
|
-
- **Parallel Migrations** -
|
|
25
|
-
- **Cross-Schema Queries** - Type-safe
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
11
|
+
- **Schema Isolation** - PostgreSQL schema-per-tenant with LRU pool management
|
|
12
|
+
- **Context Propagation** - AsyncLocalStorage-based tenant context
|
|
13
|
+
- **Parallel Migrations** - Concurrent migrations with progress tracking
|
|
14
|
+
- **Cross-Schema Queries** - Type-safe joins between tenant and shared tables
|
|
15
|
+
- **Connection Retry** - Automatic retry with exponential backoff
|
|
16
|
+
- **Framework Support** - Express, Fastify, NestJS middleware/plugins
|
|
17
|
+
- **CLI Tools** - Generate, migrate, status, tenant management
|
|
28
18
|
|
|
29
19
|
## Installation
|
|
30
20
|
|
|
@@ -34,43 +24,23 @@ npm install drizzle-multitenant drizzle-orm pg
|
|
|
34
24
|
|
|
35
25
|
## Quick Start
|
|
36
26
|
|
|
37
|
-
### 1. Define your configuration
|
|
38
|
-
|
|
39
27
|
```typescript
|
|
40
28
|
// tenant.config.ts
|
|
41
29
|
import { defineConfig } from 'drizzle-multitenant';
|
|
42
30
|
import * as tenantSchema from './schemas/tenant';
|
|
43
|
-
import * as sharedSchema from './schemas/shared';
|
|
44
31
|
|
|
45
32
|
export default defineConfig({
|
|
46
|
-
connection: {
|
|
47
|
-
url: process.env.DATABASE_URL!,
|
|
48
|
-
},
|
|
33
|
+
connection: { url: process.env.DATABASE_URL! },
|
|
49
34
|
isolation: {
|
|
50
35
|
strategy: 'schema',
|
|
51
|
-
schemaNameTemplate: (
|
|
52
|
-
maxPools: 50,
|
|
53
|
-
poolTtlMs: 60 * 60 * 1000,
|
|
54
|
-
},
|
|
55
|
-
schemas: {
|
|
56
|
-
tenant: tenantSchema,
|
|
57
|
-
shared: sharedSchema,
|
|
58
|
-
},
|
|
59
|
-
// Optional: CLI migrations config
|
|
60
|
-
migrations: {
|
|
61
|
-
tenantFolder: './drizzle/tenant',
|
|
62
|
-
migrationsTable: '__drizzle_migrations', // Custom table name
|
|
63
|
-
tenantDiscovery: async () => {
|
|
64
|
-
// Return list of tenant IDs for migrations
|
|
65
|
-
return ['tenant-1', 'tenant-2'];
|
|
66
|
-
},
|
|
36
|
+
schemaNameTemplate: (id) => `tenant_${id}`,
|
|
67
37
|
},
|
|
38
|
+
schemas: { tenant: tenantSchema },
|
|
68
39
|
});
|
|
69
40
|
```
|
|
70
41
|
|
|
71
|
-
### 2. Create the tenant manager
|
|
72
|
-
|
|
73
42
|
```typescript
|
|
43
|
+
// app.ts
|
|
74
44
|
import { createTenantManager } from 'drizzle-multitenant';
|
|
75
45
|
import config from './tenant.config';
|
|
76
46
|
|
|
@@ -80,329 +50,47 @@ const tenants = createTenantManager(config);
|
|
|
80
50
|
const db = tenants.getDb('tenant-123');
|
|
81
51
|
const users = await db.select().from(schema.users);
|
|
82
52
|
|
|
83
|
-
//
|
|
84
|
-
const
|
|
85
|
-
const plans = await shared.select().from(sharedSchema.plans);
|
|
53
|
+
// With retry and validation
|
|
54
|
+
const db = await tenants.getDbAsync('tenant-123');
|
|
86
55
|
```
|
|
87
56
|
|
|
88
|
-
|
|
57
|
+
## CLI
|
|
89
58
|
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Run code within tenant context
|
|
96
|
-
await ctx.runWithTenant({ tenantId: 'tenant-123' }, async () => {
|
|
97
|
-
const db = ctx.getTenantDb();
|
|
98
|
-
// All queries automatically scoped to tenant
|
|
99
|
-
const users = await db.select().from(schema.users);
|
|
100
|
-
});
|
|
59
|
+
```bash
|
|
60
|
+
npx drizzle-multitenant init # Setup wizard
|
|
61
|
+
npx drizzle-multitenant generate --name=users # Create migration
|
|
62
|
+
npx drizzle-multitenant migrate --all # Apply to all tenants
|
|
63
|
+
npx drizzle-multitenant status # Check status
|
|
101
64
|
```
|
|
102
65
|
|
|
103
66
|
## Framework Integrations
|
|
104
67
|
|
|
105
|
-
### Express
|
|
106
|
-
|
|
107
68
|
```typescript
|
|
69
|
+
// Express
|
|
108
70
|
import { createExpressMiddleware } from 'drizzle-multitenant/express';
|
|
71
|
+
app.use(createExpressMiddleware({ manager: tenants, extractTenantId: (req) => req.headers['x-tenant-id'] }));
|
|
109
72
|
|
|
110
|
-
|
|
111
|
-
manager: tenants,
|
|
112
|
-
extractTenantId: (req) => req.headers['x-tenant-id'] as string,
|
|
113
|
-
validateTenant: async (id) => checkTenantExists(id),
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
app.use('/api/:tenantId/*', middleware);
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Fastify
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
73
|
+
// Fastify
|
|
122
74
|
import { fastifyTenantPlugin } from 'drizzle-multitenant/fastify';
|
|
75
|
+
fastify.register(fastifyTenantPlugin, { manager: tenants, extractTenantId: (req) => req.headers['x-tenant-id'] });
|
|
123
76
|
|
|
124
|
-
|
|
125
|
-
manager: tenants,
|
|
126
|
-
extractTenantId: (req) => req.headers['x-tenant-id'] as string,
|
|
127
|
-
});
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### NestJS
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
77
|
+
// NestJS
|
|
133
78
|
import { TenantModule, InjectTenantDb } from 'drizzle-multitenant/nestjs';
|
|
134
|
-
|
|
135
|
-
@Module({
|
|
136
|
-
imports: [
|
|
137
|
-
TenantModule.forRoot({
|
|
138
|
-
config: tenantConfig,
|
|
139
|
-
extractTenantId: (req) => req.headers['x-tenant-id'],
|
|
140
|
-
isGlobal: true,
|
|
141
|
-
}),
|
|
142
|
-
],
|
|
143
|
-
})
|
|
144
|
-
export class AppModule {}
|
|
145
|
-
|
|
146
|
-
@Injectable({ scope: Scope.REQUEST })
|
|
147
|
-
export class UserService {
|
|
148
|
-
constructor(@InjectTenantDb() private readonly db: TenantDb) {}
|
|
149
|
-
|
|
150
|
-
async findAll() {
|
|
151
|
-
return this.db.select().from(users);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
#### Singleton Services (Cron Jobs, Event Handlers)
|
|
157
|
-
|
|
158
|
-
Use `TenantDbFactory` when you need to access tenant databases from singleton services:
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
import { TenantDbFactory, InjectTenantDbFactory } from 'drizzle-multitenant/nestjs';
|
|
162
|
-
|
|
163
|
-
@Injectable() // Singleton - no scope needed
|
|
164
|
-
export class ReportService {
|
|
165
|
-
constructor(@InjectTenantDbFactory() private dbFactory: TenantDbFactory) {}
|
|
166
|
-
|
|
167
|
-
async generateReport(tenantId: string) {
|
|
168
|
-
const db = this.dbFactory.getDb(tenantId);
|
|
169
|
-
return db.select().from(reports);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Cron job example
|
|
174
|
-
@Injectable()
|
|
175
|
-
export class DailyReportCron {
|
|
176
|
-
constructor(@InjectTenantDbFactory() private dbFactory: TenantDbFactory) {}
|
|
177
|
-
|
|
178
|
-
@Cron('0 8 * * *')
|
|
179
|
-
async run() {
|
|
180
|
-
const tenants = await this.getTenantIds();
|
|
181
|
-
for (const tenantId of tenants) {
|
|
182
|
-
const db = this.dbFactory.getDb(tenantId);
|
|
183
|
-
await this.processReports(db);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
79
|
+
@Module({ imports: [TenantModule.forRoot({ config, extractTenantId: (req) => req.headers['x-tenant-id'] })] })
|
|
187
80
|
```
|
|
188
81
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
The injected `TenantDb` provides debug utilities:
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
// Console output shows useful info
|
|
195
|
-
console.log(tenantDb);
|
|
196
|
-
// [TenantDb] tenant=123 schema=empresa_123
|
|
197
|
-
|
|
198
|
-
// Access debug information
|
|
199
|
-
console.log(tenantDb.__debug);
|
|
200
|
-
// { tenantId: '123', schemaName: 'empresa_123', isProxy: true, poolCount: 5 }
|
|
201
|
-
|
|
202
|
-
// Quick access
|
|
203
|
-
console.log(tenantDb.__tenantId); // '123'
|
|
204
|
-
|
|
205
|
-
// In tests
|
|
206
|
-
expect(tenantDb.__tenantId).toBe('expected-tenant');
|
|
207
|
-
```
|
|
82
|
+
## Documentation
|
|
208
83
|
|
|
209
|
-
|
|
84
|
+
**[Read the full documentation →](https://mateusflorez.github.io/drizzle-multitenant/)**
|
|
210
85
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
npx drizzle-multitenant migrate --all --concurrency=10
|
|
220
|
-
|
|
221
|
-
# Interactive tenant selection
|
|
222
|
-
npx drizzle-multitenant migrate # Shows checkbox to select tenants
|
|
223
|
-
|
|
224
|
-
# Check migration status
|
|
225
|
-
npx drizzle-multitenant status
|
|
226
|
-
|
|
227
|
-
# Create a new tenant schema
|
|
228
|
-
npx drizzle-multitenant tenant:create --id=new-tenant
|
|
229
|
-
|
|
230
|
-
# Drop a tenant schema
|
|
231
|
-
npx drizzle-multitenant tenant:drop --id=old-tenant --force
|
|
232
|
-
|
|
233
|
-
# Convert migration table format
|
|
234
|
-
npx drizzle-multitenant convert-format --to=name --dry-run
|
|
235
|
-
|
|
236
|
-
# Generate shell completions
|
|
237
|
-
npx drizzle-multitenant completion bash >> ~/.bashrc
|
|
238
|
-
npx drizzle-multitenant completion zsh >> ~/.zshrc
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Global Options
|
|
242
|
-
|
|
243
|
-
```bash
|
|
244
|
-
--json # Output as JSON (for scripts/CI)
|
|
245
|
-
--verbose # Show detailed output
|
|
246
|
-
--quiet # Only show errors
|
|
247
|
-
--no-color # Disable colored output
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### JSON Output
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
# Get status as JSON for scripting
|
|
254
|
-
npx drizzle-multitenant status --json | jq '.tenants[] | select(.pending > 0)'
|
|
255
|
-
|
|
256
|
-
# Parse migration results
|
|
257
|
-
npx drizzle-multitenant migrate --all --json | jq '.summary'
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Status Output
|
|
261
|
-
|
|
262
|
-
```
|
|
263
|
-
┌──────────────────┬──────────────┬────────────┬─────────┬─────────┬──────────┐
|
|
264
|
-
│ Tenant │ Schema │ Format │ Applied │ Pending │ Status │
|
|
265
|
-
├──────────────────┼──────────────┼────────────┼─────────┼─────────┼──────────┤
|
|
266
|
-
│ abc-123 │ tenant_abc │ drizzle-kit│ 45 │ 3 │ Behind │
|
|
267
|
-
│ def-456 │ tenant_def │ name │ 48 │ 0 │ OK │
|
|
268
|
-
│ ghi-789 │ tenant_ghi │ (new) │ 0 │ 48 │ Behind │
|
|
269
|
-
└──────────────────┴──────────────┴────────────┴─────────┴─────────┴──────────┘
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
## Migration Table Formats
|
|
273
|
-
|
|
274
|
-
`drizzle-multitenant` supports multiple migration table formats for compatibility with existing databases:
|
|
275
|
-
|
|
276
|
-
### Supported Formats
|
|
277
|
-
|
|
278
|
-
| Format | Identifier | Timestamp | Compatible With |
|
|
279
|
-
|--------|------------|-----------|-----------------|
|
|
280
|
-
| `name` | Filename | `applied_at` (timestamp) | drizzle-multitenant native |
|
|
281
|
-
| `hash` | SHA-256 | `created_at` (timestamp) | Custom scripts |
|
|
282
|
-
| `drizzle-kit` | SHA-256 | `created_at` (bigint) | drizzle-kit migrate |
|
|
283
|
-
|
|
284
|
-
### Configuration
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
// tenant.config.ts
|
|
288
|
-
export default defineConfig({
|
|
289
|
-
// ...
|
|
290
|
-
migrations: {
|
|
291
|
-
tenantFolder: './drizzle/tenant',
|
|
292
|
-
tenantDiscovery: async () => getTenantIds(),
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Table format for tracking migrations
|
|
296
|
-
* - "auto": Auto-detect existing format (default)
|
|
297
|
-
* - "name": Filename-based (drizzle-multitenant native)
|
|
298
|
-
* - "hash": SHA-256 hash
|
|
299
|
-
* - "drizzle-kit": Exact drizzle-kit format
|
|
300
|
-
*/
|
|
301
|
-
tableFormat: 'auto',
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Format to use when creating new tables (only for "auto" mode)
|
|
305
|
-
* @default "name"
|
|
306
|
-
*/
|
|
307
|
-
defaultFormat: 'name',
|
|
308
|
-
},
|
|
309
|
-
});
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### Migrating from drizzle-kit
|
|
313
|
-
|
|
314
|
-
If you have existing databases with migrations applied via `drizzle-kit migrate`, the CLI will automatically detect the format:
|
|
315
|
-
|
|
316
|
-
```bash
|
|
317
|
-
# Check current format for all tenants
|
|
318
|
-
npx drizzle-multitenant status
|
|
319
|
-
|
|
320
|
-
# Apply new migrations (works with any format)
|
|
321
|
-
npx drizzle-multitenant migrate --all
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
### Converting Between Formats
|
|
325
|
-
|
|
326
|
-
Use the `convert-format` command to standardize all tenants to a single format:
|
|
327
|
-
|
|
328
|
-
```bash
|
|
329
|
-
# Preview conversion (dry-run)
|
|
330
|
-
npx drizzle-multitenant convert-format --to=name --dry-run
|
|
331
|
-
|
|
332
|
-
# Convert all tenants to name format
|
|
333
|
-
npx drizzle-multitenant convert-format --to=name
|
|
334
|
-
|
|
335
|
-
# Convert specific tenant
|
|
336
|
-
npx drizzle-multitenant convert-format --to=name --tenant=abc-123
|
|
337
|
-
|
|
338
|
-
# Convert to drizzle-kit format (for compatibility)
|
|
339
|
-
npx drizzle-multitenant convert-format --to=drizzle-kit
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
## Cross-Schema Queries
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
import { createCrossSchemaQuery } from 'drizzle-multitenant/cross-schema';
|
|
346
|
-
|
|
347
|
-
const query = createCrossSchemaQuery({
|
|
348
|
-
tenantDb: tenants.getDb('tenant-123'),
|
|
349
|
-
sharedDb: tenants.getSharedDb(),
|
|
350
|
-
tenantSchema: 'tenant_123',
|
|
351
|
-
sharedSchema: 'public',
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
// Type-safe join between tenant and shared tables
|
|
355
|
-
const result = await query
|
|
356
|
-
.select({
|
|
357
|
-
orderId: orders.id,
|
|
358
|
-
planName: subscriptionPlans.name,
|
|
359
|
-
})
|
|
360
|
-
.from(orders)
|
|
361
|
-
.leftJoin(subscriptionPlans, eq(orders.planId, subscriptionPlans.id));
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
## API Reference
|
|
365
|
-
|
|
366
|
-
### Core
|
|
367
|
-
|
|
368
|
-
| Export | Description |
|
|
369
|
-
|--------|-------------|
|
|
370
|
-
| `defineConfig()` | Create typed configuration |
|
|
371
|
-
| `createTenantManager()` | Create pool manager instance |
|
|
372
|
-
| `createTenantContext()` | Create AsyncLocalStorage context |
|
|
373
|
-
|
|
374
|
-
### Manager Methods
|
|
375
|
-
|
|
376
|
-
| Method | Description |
|
|
377
|
-
|--------|-------------|
|
|
378
|
-
| `getDb(tenantId)` | Get Drizzle instance for tenant |
|
|
379
|
-
| `getSharedDb()` | Get Drizzle instance for shared schema |
|
|
380
|
-
| `getSchemaName(tenantId)` | Get schema name for tenant |
|
|
381
|
-
| `hasPool(tenantId)` | Check if pool exists |
|
|
382
|
-
| `evictPool(tenantId)` | Force evict a pool |
|
|
383
|
-
| `dispose()` | Cleanup all pools |
|
|
384
|
-
|
|
385
|
-
### NestJS Decorators
|
|
386
|
-
|
|
387
|
-
| Decorator | Description |
|
|
388
|
-
|-----------|-------------|
|
|
389
|
-
| `@InjectTenantDb()` | Inject tenant database (request-scoped) |
|
|
390
|
-
| `@InjectTenantDbFactory()` | Inject factory for singleton services |
|
|
391
|
-
| `@InjectSharedDb()` | Inject shared database |
|
|
392
|
-
| `@InjectTenantContext()` | Inject tenant context |
|
|
393
|
-
| `@InjectTenantManager()` | Inject tenant manager |
|
|
394
|
-
| `@RequiresTenant()` | Mark route as requiring tenant |
|
|
395
|
-
| `@PublicRoute()` | Mark route as public |
|
|
396
|
-
|
|
397
|
-
### TenantDbFactory Methods
|
|
398
|
-
|
|
399
|
-
| Method | Description |
|
|
400
|
-
|--------|-------------|
|
|
401
|
-
| `getDb(tenantId)` | Get Drizzle instance for tenant |
|
|
402
|
-
| `getSharedDb()` | Get shared database instance |
|
|
403
|
-
| `getSchemaName(tenantId)` | Get schema name for tenant |
|
|
404
|
-
| `getDebugInfo(tenantId)` | Get debug info (tenantId, schema, pool stats) |
|
|
405
|
-
| `getManager()` | Get underlying TenantManager |
|
|
86
|
+
- [Getting Started](https://mateusflorez.github.io/drizzle-multitenant/guide/getting-started)
|
|
87
|
+
- [Configuration](https://mateusflorez.github.io/drizzle-multitenant/guide/configuration)
|
|
88
|
+
- [Framework Integrations](https://mateusflorez.github.io/drizzle-multitenant/guide/frameworks/express)
|
|
89
|
+
- [CLI Commands](https://mateusflorez.github.io/drizzle-multitenant/guide/cli)
|
|
90
|
+
- [Cross-Schema Queries](https://mateusflorez.github.io/drizzle-multitenant/guide/cross-schema)
|
|
91
|
+
- [Advanced Features](https://mateusflorez.github.io/drizzle-multitenant/guide/advanced)
|
|
92
|
+
- [API Reference](https://mateusflorez.github.io/drizzle-multitenant/api/reference)
|
|
93
|
+
- [Examples](https://mateusflorez.github.io/drizzle-multitenant/examples/)
|
|
406
94
|
|
|
407
95
|
## Requirements
|
|
408
96
|
|
|
@@ -410,30 +98,6 @@ const result = await query
|
|
|
410
98
|
- PostgreSQL 12+
|
|
411
99
|
- Drizzle ORM 0.29+
|
|
412
100
|
|
|
413
|
-
## Tech Stack
|
|
414
|
-
|
|
415
|
-
| Package | Purpose |
|
|
416
|
-
|---------|---------|
|
|
417
|
-
| `drizzle-orm` | Type-safe ORM |
|
|
418
|
-
| `pg` | PostgreSQL driver |
|
|
419
|
-
| `lru-cache` | Pool management |
|
|
420
|
-
| `commander` | CLI framework |
|
|
421
|
-
| `chalk` | Terminal styling |
|
|
422
|
-
| `ora` | Loading spinners |
|
|
423
|
-
| `cli-table3` | Table formatting |
|
|
424
|
-
| `cli-progress` | Progress bars |
|
|
425
|
-
| `@inquirer/prompts` | Interactive prompts |
|
|
426
|
-
|
|
427
|
-
## Comparison
|
|
428
|
-
|
|
429
|
-
| Feature | drizzle-multitenant | Manual Implementation |
|
|
430
|
-
|---------|---------------------|----------------------|
|
|
431
|
-
| Pool management | Automatic LRU | Manual |
|
|
432
|
-
| Context propagation | AsyncLocalStorage | Pass through params |
|
|
433
|
-
| Parallel migrations | Built-in CLI | Custom scripts |
|
|
434
|
-
| Cross-schema queries | Type-safe builder | Raw SQL |
|
|
435
|
-
| Framework support | Express/Fastify/NestJS/Hono | DIY |
|
|
436
|
-
|
|
437
101
|
## License
|
|
438
102
|
|
|
439
103
|
MIT
|