@veloxts/orm 0.1.0
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 +21 -0
- package/README.md +566 -0
- package/dist/client.d.ts +114 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +156 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +117 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +138 -0
- package/dist/plugin.js.map +1 -0
- package/dist/types.d.ts +146 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +35 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 VeloxTS Framework Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# @veloxts/orm
|
|
2
|
+
|
|
3
|
+
Laravel-inspired Prisma wrapper with enhanced developer experience for the VeloxTS framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @veloxts/orm @prisma/client
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @veloxts/orm @prisma/client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Note: `@prisma/client` is a peer dependency. You'll also need the `prisma` CLI as a dev dependency:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -D prisma
|
|
17
|
+
# or
|
|
18
|
+
pnpm add -D prisma
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Initialize Prisma
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx prisma init
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This creates a `prisma` directory with a `schema.prisma` file.
|
|
30
|
+
|
|
31
|
+
### 2. Define Your Schema
|
|
32
|
+
|
|
33
|
+
Edit `prisma/schema.prisma`:
|
|
34
|
+
|
|
35
|
+
```prisma
|
|
36
|
+
datasource db {
|
|
37
|
+
provider = "postgresql"
|
|
38
|
+
url = env("DATABASE_URL")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
generator client {
|
|
42
|
+
provider = "prisma-client-js"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
model User {
|
|
46
|
+
id String @id @default(uuid())
|
|
47
|
+
name String
|
|
48
|
+
email String @unique
|
|
49
|
+
createdAt DateTime @default(now())
|
|
50
|
+
updatedAt DateTime @updatedAt
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Generate Prisma Client
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx prisma generate
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 4. Integrate with VeloxTS
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { createVeloxApp } from '@veloxts/core';
|
|
64
|
+
import { createDatabasePlugin } from '@veloxts/orm';
|
|
65
|
+
import { PrismaClient } from '@prisma/client';
|
|
66
|
+
|
|
67
|
+
// Create Prisma client
|
|
68
|
+
const prisma = new PrismaClient();
|
|
69
|
+
|
|
70
|
+
// Create VeloxTS app
|
|
71
|
+
const app = await createVeloxApp({
|
|
72
|
+
port: 3000,
|
|
73
|
+
logger: true,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Register database plugin
|
|
77
|
+
await app.register(createDatabasePlugin({ client: prisma }));
|
|
78
|
+
|
|
79
|
+
// Start server
|
|
80
|
+
await app.start();
|
|
81
|
+
console.log(`Server running on ${app.address}`);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Core API
|
|
85
|
+
|
|
86
|
+
### `createDatabasePlugin(config)`
|
|
87
|
+
|
|
88
|
+
Creates a VeloxTS plugin that integrates Prisma with automatic lifecycle management.
|
|
89
|
+
|
|
90
|
+
**Configuration:**
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
interface OrmPluginConfig {
|
|
94
|
+
client: PrismaClient; // Your Prisma client instance
|
|
95
|
+
connect?: boolean; // Auto-connect on startup (default: true)
|
|
96
|
+
disconnect?: boolean; // Auto-disconnect on shutdown (default: true)
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Example:**
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { createDatabasePlugin } from '@veloxts/orm';
|
|
104
|
+
import { PrismaClient } from '@prisma/client';
|
|
105
|
+
|
|
106
|
+
const prisma = new PrismaClient({
|
|
107
|
+
log: ['query', 'error', 'warn'],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const dbPlugin = createDatabasePlugin({
|
|
111
|
+
client: prisma,
|
|
112
|
+
connect: true, // Connect when app starts
|
|
113
|
+
disconnect: true, // Disconnect on graceful shutdown
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await app.register(dbPlugin);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Context Extension
|
|
120
|
+
|
|
121
|
+
The database plugin extends the VeloxTS context with a `db` property:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Type definition is automatically available
|
|
125
|
+
declare module '@veloxts/core' {
|
|
126
|
+
interface BaseContext {
|
|
127
|
+
db: PrismaClient;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Use in procedures
|
|
132
|
+
export const userProcedures = defineProcedures('users', {
|
|
133
|
+
getUser: procedure()
|
|
134
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
135
|
+
.output(UserSchema)
|
|
136
|
+
.query(async ({ input, ctx }) => {
|
|
137
|
+
// ctx.db is fully typed as PrismaClient
|
|
138
|
+
const user = await ctx.db.user.findUnique({
|
|
139
|
+
where: { id: input.id },
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
if (!user) {
|
|
143
|
+
throw new NotFoundError('User', input.id);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return user;
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Database Queries
|
|
152
|
+
|
|
153
|
+
The `ctx.db` object provides full access to Prisma's query API:
|
|
154
|
+
|
|
155
|
+
### Finding Records
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// Find unique record
|
|
159
|
+
const user = await ctx.db.user.findUnique({
|
|
160
|
+
where: { id: userId },
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Find unique or throw
|
|
164
|
+
const user = await ctx.db.user.findUniqueOrThrow({
|
|
165
|
+
where: { id: userId },
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Find first matching record
|
|
169
|
+
const user = await ctx.db.user.findFirst({
|
|
170
|
+
where: { email: 'alice@example.com' },
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Find many with filters
|
|
174
|
+
const users = await ctx.db.user.findMany({
|
|
175
|
+
where: {
|
|
176
|
+
email: { contains: '@example.com' },
|
|
177
|
+
createdAt: { gte: new Date('2025-01-01') },
|
|
178
|
+
},
|
|
179
|
+
orderBy: { createdAt: 'desc' },
|
|
180
|
+
take: 10,
|
|
181
|
+
skip: 0,
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Creating Records
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// Create single record
|
|
189
|
+
const user = await ctx.db.user.create({
|
|
190
|
+
data: {
|
|
191
|
+
name: 'Alice',
|
|
192
|
+
email: 'alice@example.com',
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Create with relations
|
|
197
|
+
const post = await ctx.db.post.create({
|
|
198
|
+
data: {
|
|
199
|
+
title: 'Hello World',
|
|
200
|
+
content: 'My first post',
|
|
201
|
+
author: {
|
|
202
|
+
connect: { id: userId },
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
include: { author: true }, // Include related data
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Updating Records
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// Update single record
|
|
213
|
+
const user = await ctx.db.user.update({
|
|
214
|
+
where: { id: userId },
|
|
215
|
+
data: { name: 'Alice Smith' },
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Update many
|
|
219
|
+
const result = await ctx.db.user.updateMany({
|
|
220
|
+
where: { email: { contains: '@old-domain.com' } },
|
|
221
|
+
data: { email: 'migrated@new-domain.com' },
|
|
222
|
+
});
|
|
223
|
+
console.log(`Updated ${result.count} users`);
|
|
224
|
+
|
|
225
|
+
// Upsert (update or create)
|
|
226
|
+
const user = await ctx.db.user.upsert({
|
|
227
|
+
where: { email: 'alice@example.com' },
|
|
228
|
+
update: { name: 'Alice Updated' },
|
|
229
|
+
create: {
|
|
230
|
+
name: 'Alice',
|
|
231
|
+
email: 'alice@example.com',
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Deleting Records
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// Delete single record
|
|
240
|
+
const user = await ctx.db.user.delete({
|
|
241
|
+
where: { id: userId },
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Delete many
|
|
245
|
+
const result = await ctx.db.user.deleteMany({
|
|
246
|
+
where: { createdAt: { lt: new Date('2024-01-01') } },
|
|
247
|
+
});
|
|
248
|
+
console.log(`Deleted ${result.count} users`);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Aggregations and Counting
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
// Count records
|
|
255
|
+
const count = await ctx.db.user.count({
|
|
256
|
+
where: { email: { contains: '@example.com' } },
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Aggregate
|
|
260
|
+
const stats = await ctx.db.post.aggregate({
|
|
261
|
+
_count: true,
|
|
262
|
+
_avg: { views: true },
|
|
263
|
+
_sum: { views: true },
|
|
264
|
+
where: { published: true },
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Transactions
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// Transaction with array of operations
|
|
272
|
+
const [user, post] = await ctx.db.$transaction([
|
|
273
|
+
ctx.db.user.create({ data: { name: 'Alice', email: 'alice@example.com' } }),
|
|
274
|
+
ctx.db.post.create({ data: { title: 'Hello', content: 'World' } }),
|
|
275
|
+
]);
|
|
276
|
+
|
|
277
|
+
// Interactive transaction
|
|
278
|
+
const result = await ctx.db.$transaction(async (tx) => {
|
|
279
|
+
const user = await tx.user.create({
|
|
280
|
+
data: { name: 'Bob', email: 'bob@example.com' },
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const post = await tx.post.create({
|
|
284
|
+
data: {
|
|
285
|
+
title: 'Bob\'s Post',
|
|
286
|
+
content: 'Hello from Bob',
|
|
287
|
+
authorId: user.id,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
return { user, post };
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Database Migrations
|
|
296
|
+
|
|
297
|
+
VeloxTS provides CLI commands for managing database migrations via Prisma:
|
|
298
|
+
|
|
299
|
+
### Development Workflow
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
# Create migration after schema changes
|
|
303
|
+
npx prisma migrate dev --name add_user_table
|
|
304
|
+
|
|
305
|
+
# Apply migrations
|
|
306
|
+
velox migrate
|
|
307
|
+
|
|
308
|
+
# Force push schema (dev only, skips migrations)
|
|
309
|
+
velox migrate --force
|
|
310
|
+
|
|
311
|
+
# Reset database (WARNING: deletes all data)
|
|
312
|
+
npx prisma migrate reset
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Production Workflow
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Deploy pending migrations
|
|
319
|
+
velox migrate --deploy
|
|
320
|
+
|
|
321
|
+
# Or use Prisma directly
|
|
322
|
+
npx prisma migrate deploy
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Manual Connection Management
|
|
326
|
+
|
|
327
|
+
For advanced use cases, you can manually manage connections:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
import { createDatabase } from '@veloxts/orm';
|
|
331
|
+
import { PrismaClient } from '@prisma/client';
|
|
332
|
+
|
|
333
|
+
const db = createDatabase({
|
|
334
|
+
client: new PrismaClient(),
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// Manually connect
|
|
338
|
+
await db.connect();
|
|
339
|
+
console.log('Connected:', db.isConnected);
|
|
340
|
+
|
|
341
|
+
// Use the client
|
|
342
|
+
const users = await db.client.user.findMany();
|
|
343
|
+
|
|
344
|
+
// Check connection status
|
|
345
|
+
const status = db.getStatus();
|
|
346
|
+
console.log(status);
|
|
347
|
+
// {
|
|
348
|
+
// state: 'connected',
|
|
349
|
+
// connectedAt: Date,
|
|
350
|
+
// disconnectedAt: null,
|
|
351
|
+
// errorCount: 0,
|
|
352
|
+
// lastError: null
|
|
353
|
+
// }
|
|
354
|
+
|
|
355
|
+
// Manually disconnect
|
|
356
|
+
await db.disconnect();
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Practical Examples
|
|
360
|
+
|
|
361
|
+
### CRUD Procedures
|
|
362
|
+
|
|
363
|
+
Complete example of CRUD operations:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import { defineProcedures, procedure } from '@veloxts/router';
|
|
367
|
+
import { z, paginationInputSchema } from '@veloxts/validation';
|
|
368
|
+
import { NotFoundError } from '@veloxts/core';
|
|
369
|
+
|
|
370
|
+
const UserSchema = z.object({
|
|
371
|
+
id: z.string().uuid(),
|
|
372
|
+
name: z.string(),
|
|
373
|
+
email: z.string().email(),
|
|
374
|
+
createdAt: z.string().datetime(),
|
|
375
|
+
updatedAt: z.string().datetime(),
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
export const userProcedures = defineProcedures('users', {
|
|
379
|
+
// GET /users/:id
|
|
380
|
+
getUser: procedure()
|
|
381
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
382
|
+
.output(UserSchema)
|
|
383
|
+
.query(async ({ input, ctx }) => {
|
|
384
|
+
const user = await ctx.db.user.findUnique({
|
|
385
|
+
where: { id: input.id },
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
if (!user) {
|
|
389
|
+
throw new NotFoundError('User', input.id);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return user;
|
|
393
|
+
}),
|
|
394
|
+
|
|
395
|
+
// GET /users
|
|
396
|
+
listUsers: procedure()
|
|
397
|
+
.input(paginationInputSchema)
|
|
398
|
+
.output(z.object({
|
|
399
|
+
data: z.array(UserSchema),
|
|
400
|
+
meta: z.object({
|
|
401
|
+
page: z.number(),
|
|
402
|
+
limit: z.number(),
|
|
403
|
+
total: z.number(),
|
|
404
|
+
}),
|
|
405
|
+
}))
|
|
406
|
+
.query(async ({ input, ctx }) => {
|
|
407
|
+
const skip = (input.page - 1) * input.limit;
|
|
408
|
+
|
|
409
|
+
const [data, total] = await Promise.all([
|
|
410
|
+
ctx.db.user.findMany({
|
|
411
|
+
skip,
|
|
412
|
+
take: input.limit,
|
|
413
|
+
orderBy: { createdAt: 'desc' },
|
|
414
|
+
}),
|
|
415
|
+
ctx.db.user.count(),
|
|
416
|
+
]);
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
data,
|
|
420
|
+
meta: { page: input.page, limit: input.limit, total },
|
|
421
|
+
};
|
|
422
|
+
}),
|
|
423
|
+
|
|
424
|
+
// POST /users
|
|
425
|
+
createUser: procedure()
|
|
426
|
+
.input(z.object({
|
|
427
|
+
name: z.string().min(1),
|
|
428
|
+
email: z.string().email(),
|
|
429
|
+
}))
|
|
430
|
+
.output(UserSchema)
|
|
431
|
+
.mutation(async ({ input, ctx }) => {
|
|
432
|
+
return ctx.db.user.create({ data: input });
|
|
433
|
+
}),
|
|
434
|
+
|
|
435
|
+
// PUT /users/:id (deferred to v1.1+)
|
|
436
|
+
// DELETE /users/:id (deferred to v1.1+)
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Relationships
|
|
441
|
+
|
|
442
|
+
Working with related data:
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
// Schema with relations
|
|
446
|
+
model User {
|
|
447
|
+
id String @id @default(uuid())
|
|
448
|
+
name String
|
|
449
|
+
posts Post[]
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
model Post {
|
|
453
|
+
id String @id @default(uuid())
|
|
454
|
+
title String
|
|
455
|
+
authorId String
|
|
456
|
+
author User @relation(fields: [authorId], references: [id])
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Query with relations
|
|
460
|
+
const userProcedures = defineProcedures('users', {
|
|
461
|
+
getUserWithPosts: procedure()
|
|
462
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
463
|
+
.query(async ({ input, ctx }) => {
|
|
464
|
+
return ctx.db.user.findUnique({
|
|
465
|
+
where: { id: input.id },
|
|
466
|
+
include: {
|
|
467
|
+
posts: {
|
|
468
|
+
orderBy: { createdAt: 'desc' },
|
|
469
|
+
take: 10,
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
}),
|
|
474
|
+
});
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## Configuration Best Practices
|
|
478
|
+
|
|
479
|
+
### Environment Variables
|
|
480
|
+
|
|
481
|
+
Store connection strings in `.env`:
|
|
482
|
+
|
|
483
|
+
```env
|
|
484
|
+
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Production Setup
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
const prisma = new PrismaClient({
|
|
491
|
+
log: process.env.NODE_ENV === 'development'
|
|
492
|
+
? ['query', 'error', 'warn']
|
|
493
|
+
: ['error'],
|
|
494
|
+
errorFormat: 'minimal',
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const dbPlugin = createDatabasePlugin({
|
|
498
|
+
client: prisma,
|
|
499
|
+
connect: true,
|
|
500
|
+
disconnect: true,
|
|
501
|
+
});
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Connection Pooling
|
|
505
|
+
|
|
506
|
+
Prisma handles connection pooling automatically. Configure in `schema.prisma`:
|
|
507
|
+
|
|
508
|
+
```prisma
|
|
509
|
+
datasource db {
|
|
510
|
+
provider = "postgresql"
|
|
511
|
+
url = env("DATABASE_URL")
|
|
512
|
+
// Connection pool settings via URL parameters:
|
|
513
|
+
// ?connection_limit=10&pool_timeout=20
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
## Error Handling
|
|
518
|
+
|
|
519
|
+
The ORM plugin integrates with VeloxTS's error handling:
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
import { NotFoundError, VeloxError } from '@veloxts/core';
|
|
523
|
+
|
|
524
|
+
try {
|
|
525
|
+
const user = await ctx.db.user.findUniqueOrThrow({
|
|
526
|
+
where: { id: userId },
|
|
527
|
+
});
|
|
528
|
+
} catch (error) {
|
|
529
|
+
if (error.code === 'P2025') {
|
|
530
|
+
// Prisma "Record not found" error
|
|
531
|
+
throw new NotFoundError('User', userId);
|
|
532
|
+
}
|
|
533
|
+
throw new VeloxError('Database error', 500);
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## MVP Limitations
|
|
538
|
+
|
|
539
|
+
The current v0.1.0 release focuses on core functionality:
|
|
540
|
+
|
|
541
|
+
**Included:**
|
|
542
|
+
- Prisma client integration
|
|
543
|
+
- Context extension (`ctx.db`)
|
|
544
|
+
- Connection lifecycle management
|
|
545
|
+
- Basic migration commands
|
|
546
|
+
|
|
547
|
+
**Deferred to v1.1+:**
|
|
548
|
+
- Database seeding utilities
|
|
549
|
+
- Migration rollback via CLI
|
|
550
|
+
- Query logging middleware
|
|
551
|
+
- Advanced connection pooling configuration
|
|
552
|
+
|
|
553
|
+
## Related Packages
|
|
554
|
+
|
|
555
|
+
- [@veloxts/core](/packages/core) - Core framework with context system
|
|
556
|
+
- [@veloxts/router](/packages/router) - Procedure definitions using database queries
|
|
557
|
+
- [@veloxts/validation](/packages/validation) - Schema validation for inputs/outputs
|
|
558
|
+
- [@veloxts/cli](/packages/cli) - CLI with `velox migrate` command
|
|
559
|
+
|
|
560
|
+
## TypeScript Support
|
|
561
|
+
|
|
562
|
+
All exports are fully typed with comprehensive JSDoc documentation. The package includes type definitions and declaration maps for excellent IDE support.
|
|
563
|
+
|
|
564
|
+
## License
|
|
565
|
+
|
|
566
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database client wrapper for Prisma
|
|
3
|
+
*
|
|
4
|
+
* Provides lifecycle management and connection state tracking
|
|
5
|
+
* for Prisma clients in a type-safe manner.
|
|
6
|
+
*
|
|
7
|
+
* @module client
|
|
8
|
+
*/
|
|
9
|
+
import type { ConnectionStatus, DatabaseClient, DatabaseWrapperConfig } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Wrapped database client with connection lifecycle management
|
|
12
|
+
*
|
|
13
|
+
* @template TClient - Type of the underlying Prisma client
|
|
14
|
+
*/
|
|
15
|
+
export interface Database<TClient extends DatabaseClient> {
|
|
16
|
+
/**
|
|
17
|
+
* The underlying Prisma client instance
|
|
18
|
+
*
|
|
19
|
+
* Use this for all database queries. The client is always available,
|
|
20
|
+
* but queries will fail if not connected.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const db = createDatabase({ client: prisma });
|
|
25
|
+
* await db.connect();
|
|
26
|
+
*
|
|
27
|
+
* const users = await db.client.user.findMany();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
readonly client: TClient;
|
|
31
|
+
/**
|
|
32
|
+
* Current connection status
|
|
33
|
+
*/
|
|
34
|
+
readonly status: ConnectionStatus;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the database is connected
|
|
37
|
+
*
|
|
38
|
+
* Convenience getter equivalent to `status.isConnected`
|
|
39
|
+
*/
|
|
40
|
+
readonly isConnected: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Establishes connection to the database
|
|
43
|
+
*
|
|
44
|
+
* @throws {VeloxError} If already connected or connection fails
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const db = createDatabase({ client: prisma });
|
|
49
|
+
* await db.connect();
|
|
50
|
+
* console.log(db.isConnected); // true
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
connect(): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Disconnects from the database
|
|
56
|
+
*
|
|
57
|
+
* @throws {VeloxError} If not connected or disconnection fails
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* await db.disconnect();
|
|
62
|
+
* console.log(db.isConnected); // false
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
disconnect(): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates a database wrapper with connection lifecycle management
|
|
69
|
+
*
|
|
70
|
+
* This wrapper provides:
|
|
71
|
+
* - Connection state tracking
|
|
72
|
+
* - Controlled connect/disconnect methods
|
|
73
|
+
* - Type-safe access to the underlying client
|
|
74
|
+
*
|
|
75
|
+
* The client is NOT automatically connected - call `connect()` explicitly
|
|
76
|
+
* or use `createDatabasePlugin` for automatic lifecycle management.
|
|
77
|
+
*
|
|
78
|
+
* @template TClient - Type of the Prisma client
|
|
79
|
+
* @param config - Database configuration with client instance
|
|
80
|
+
* @returns Database wrapper with lifecycle management
|
|
81
|
+
*
|
|
82
|
+
* @throws {VeloxError} If config is invalid or client doesn't implement DatabaseClient
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* import { PrismaClient } from '@prisma/client';
|
|
87
|
+
* import { createDatabase } from '@veloxts/orm';
|
|
88
|
+
*
|
|
89
|
+
* const prisma = new PrismaClient();
|
|
90
|
+
* const db = createDatabase({ client: prisma });
|
|
91
|
+
*
|
|
92
|
+
* // Manual connection management
|
|
93
|
+
* await db.connect();
|
|
94
|
+
* const users = await db.client.user.findMany();
|
|
95
|
+
* await db.disconnect();
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* // Check connection status
|
|
101
|
+
* const db = createDatabase({ client: prisma });
|
|
102
|
+
*
|
|
103
|
+
* console.log(db.isConnected); // false
|
|
104
|
+
* console.log(db.status.state); // 'disconnected'
|
|
105
|
+
*
|
|
106
|
+
* await db.connect();
|
|
107
|
+
*
|
|
108
|
+
* console.log(db.isConnected); // true
|
|
109
|
+
* console.log(db.status.state); // 'connected'
|
|
110
|
+
* console.log(db.status.connectedAt); // Date
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function createDatabase<TClient extends DatabaseClient>(config: DatabaseWrapperConfig<TClient>): Database<TClient>;
|
|
114
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAEV,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAOpB;;;;GAIG;AACH,MAAM,WAAW,QAAQ,CAAC,OAAO,SAAS,cAAc;IACtD;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAEzB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAElC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B;;;;;;;;;;;OAWG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB;;;;;;;;;;OAUG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,cAAc,CAAC,OAAO,SAAS,cAAc,EAC3D,MAAM,EAAE,qBAAqB,CAAC,OAAO,CAAC,GACrC,QAAQ,CAAC,OAAO,CAAC,CAgInB"}
|