@zola_do/typeorm 0.2.5 → 0.2.7
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 +382 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
# @zola_do/typeorm
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@zola_do/typeorm)
|
|
4
|
+
[](https://www.npmjs.com/package/@zola_do/typeorm)
|
|
5
|
+
[](https://opensource.org/licenses/ISC)
|
|
6
|
+
|
|
3
7
|
TypeORM configuration helpers and service for NestJS applications.
|
|
4
8
|
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
`@zola_do/typeorm` provides:
|
|
12
|
+
|
|
13
|
+
- **Environment-based Configuration** — Database settings from environment variables
|
|
14
|
+
- **Auto-entity Discovery** — Automatically loads entities and migrations
|
|
15
|
+
- **DataSource Options** — TypeORM 0.3+ compatible configuration
|
|
16
|
+
- **Config Service** — Injectable service for database access
|
|
17
|
+
|
|
5
18
|
## Installation
|
|
6
19
|
|
|
7
20
|
```bash
|
|
@@ -12,65 +25,405 @@ npm install @zola_do/typeorm
|
|
|
12
25
|
npm install @zola_do/nestjs-shared
|
|
13
26
|
```
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
### Dependencies
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @nestjs/typeorm typeorm typeorm-extension dotenv
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Read replicas (TypeORM `replication`)
|
|
35
|
+
|
|
36
|
+
`@zola_do/typeorm` exposes `dataSourceOptions` as a starting point. For read scaling, use TypeORM's [`replication`](https://typeorm.io/data-source-options#postgres--cockroachdb--sap-hana--sql.js) option in the object you pass to `TypeOrmModule.forRoot`: supply `master` (writer) and `slaves` (readers). Environment variables are then modeled in your app (for example `DATABASE_REPLICA_HOSTS` parsed to an array) rather than hard-coded in the library.
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### 1. Configure Environment
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# .env
|
|
44
|
+
DATABASE_HOST=localhost
|
|
45
|
+
DATABASE_PORT=5432
|
|
46
|
+
DATABASE_NAME=myapp
|
|
47
|
+
DATABASE_USER=postgres
|
|
48
|
+
DATABASE_PASSWORD=secret
|
|
49
|
+
APP_NAME=myapp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. Register in AppModule
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Module } from "@nestjs/common";
|
|
56
|
+
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
57
|
+
import { dataSourceOptions } from "@zola_do/typeorm";
|
|
58
|
+
|
|
59
|
+
@Module({
|
|
60
|
+
imports: [TypeOrmModule.forRoot(dataSourceOptions)],
|
|
61
|
+
})
|
|
62
|
+
export class AppModule {}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 3. Use Entities
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { CommonEntity } from "@zola_do/nestjs-shared";
|
|
69
|
+
import { Entity, Column } from "typeorm";
|
|
70
|
+
|
|
71
|
+
@Entity("products")
|
|
72
|
+
export class Product extends CommonEntity {
|
|
73
|
+
@Column()
|
|
74
|
+
name: string;
|
|
75
|
+
|
|
76
|
+
@Column("decimal", { precision: 10, scale: 2 })
|
|
77
|
+
price: number;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Entity Configuration
|
|
16
82
|
|
|
17
|
-
###
|
|
83
|
+
### CommonEntity
|
|
18
84
|
|
|
19
|
-
|
|
85
|
+
All entities should extend `CommonEntity` for consistent audit fields:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { CommonEntity } from "@zola_do/nestjs-shared";
|
|
89
|
+
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
|
|
90
|
+
|
|
91
|
+
@Entity("products")
|
|
92
|
+
export class Product extends CommonEntity {
|
|
93
|
+
@PrimaryGeneratedColumn("uuid")
|
|
94
|
+
id: string;
|
|
95
|
+
|
|
96
|
+
@Column()
|
|
97
|
+
name: string;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### CommonEntity Fields
|
|
102
|
+
|
|
103
|
+
| Field | Type | Description |
|
|
104
|
+
| ----------- | -------- | -------------------------------- |
|
|
105
|
+
| `id` | `UUID` | Primary key (auto-generated) |
|
|
106
|
+
| `createdAt` | `Date` | Auto-set on creation |
|
|
107
|
+
| `updatedAt` | `Date` | Auto-updated on changes |
|
|
108
|
+
| `createdBy` | `string` | Nullable - user who created |
|
|
109
|
+
| `updatedBy` | `string` | Nullable - user who last updated |
|
|
110
|
+
| `deletedAt` | `Date` | Nullable - soft delete timestamp |
|
|
111
|
+
|
|
112
|
+
### Entity Patterns
|
|
113
|
+
|
|
114
|
+
#### Basic Entity
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
@Entity("categories")
|
|
118
|
+
export class Category extends CommonEntity {
|
|
119
|
+
@PrimaryGeneratedColumn("uuid")
|
|
120
|
+
id: string;
|
|
121
|
+
|
|
122
|
+
@Column()
|
|
123
|
+
name: string;
|
|
124
|
+
|
|
125
|
+
@Column({ nullable: true })
|
|
126
|
+
description: string;
|
|
127
|
+
|
|
128
|
+
@OneToMany(() => Product, (product) => product.category)
|
|
129
|
+
products: Product[];
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Entity with Relations
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
@Entity("orders")
|
|
137
|
+
export class Order extends CommonEntity {
|
|
138
|
+
@PrimaryGeneratedColumn("uuid")
|
|
139
|
+
id: string;
|
|
140
|
+
|
|
141
|
+
@Column()
|
|
142
|
+
customerId: string;
|
|
143
|
+
|
|
144
|
+
@Column("decimal", { precision: 10, scale: 2 })
|
|
145
|
+
total: number;
|
|
146
|
+
|
|
147
|
+
@Column({ default: "pending" })
|
|
148
|
+
status: string;
|
|
149
|
+
|
|
150
|
+
@ManyToOne(() => Customer, (customer) => customer.orders)
|
|
151
|
+
@JoinColumn({ name: "customerId" })
|
|
152
|
+
customer: Customer;
|
|
153
|
+
|
|
154
|
+
@OneToMany(() => OrderItem, (item) => item.order)
|
|
155
|
+
items: OrderItem[];
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Entity with Indexes
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
@Entity("products", {
|
|
163
|
+
indexes: [
|
|
164
|
+
{ name: "idx_product_name", columns: ["name"] },
|
|
165
|
+
{ name: "idx_product_status_price", columns: ["status", "price"] },
|
|
166
|
+
],
|
|
167
|
+
})
|
|
168
|
+
export class Product extends CommonEntity {
|
|
169
|
+
@Column()
|
|
170
|
+
name: string;
|
|
171
|
+
|
|
172
|
+
@Column({ default: "active" })
|
|
173
|
+
status: string;
|
|
174
|
+
|
|
175
|
+
@Column("decimal", { precision: 10, scale: 2 })
|
|
176
|
+
price: number;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## TypeOrmConfigHelper
|
|
181
|
+
|
|
182
|
+
Access individual configuration values:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { TypeOrmConfigHelper } from "@zola_do/typeorm";
|
|
186
|
+
|
|
187
|
+
console.log(TypeOrmConfigHelper.DATABASE_HOST); // 'localhost'
|
|
188
|
+
console.log(TypeOrmConfigHelper.DATABASE_PORT); // 5432
|
|
189
|
+
console.log(TypeOrmConfigHelper.DATABASE_NAME); // 'myapp'
|
|
190
|
+
console.log(TypeOrmConfigHelper.DATABASE_USER); // 'postgres'
|
|
191
|
+
console.log(TypeOrmConfigHelper.DATABASE_PASSWORD); // 'secret'
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Config Keys
|
|
195
|
+
|
|
196
|
+
| Key | Type | Description |
|
|
197
|
+
| ------------------- | -------- | ------------------------------------- |
|
|
198
|
+
| `DATABASE_HOST` | `string` | Database host |
|
|
199
|
+
| `DATABASE_PORT` | `number` | Database port |
|
|
200
|
+
| `DATABASE_NAME` | `string` | Database name |
|
|
201
|
+
| `DATABASE_USER` | `string` | Database user |
|
|
202
|
+
| `DATABASE_PASSWORD` | `string` | Database password |
|
|
203
|
+
| `APP_NAME` | `string` | App name (fallback for DATABASE_NAME) |
|
|
204
|
+
|
|
205
|
+
## TypeOrmService
|
|
206
|
+
|
|
207
|
+
Injectable service for direct database access:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { Injectable } from "@nestjs/common";
|
|
211
|
+
import { TypeOrmService } from "@zola_do/typeorm";
|
|
212
|
+
|
|
213
|
+
@Injectable()
|
|
214
|
+
export class AnalyticsService {
|
|
215
|
+
constructor(private readonly typeorm: TypeOrmService) {}
|
|
216
|
+
|
|
217
|
+
async getTableStats() {
|
|
218
|
+
return await this.typeorm.query(`
|
|
219
|
+
SELECT
|
|
220
|
+
schemaname,
|
|
221
|
+
relname,
|
|
222
|
+
n_tup_ins as inserts,
|
|
223
|
+
n_tup_upd as updates,
|
|
224
|
+
n_tup_del as deletes
|
|
225
|
+
FROM pg_stat_user_tables
|
|
226
|
+
WHERE schemaname = 'public'
|
|
227
|
+
`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## DataSource Options
|
|
233
|
+
|
|
234
|
+
The `dataSourceOptions` object provides full TypeORM configuration:
|
|
20
235
|
|
|
21
236
|
```typescript
|
|
22
|
-
import { Module } from '@nestjs/common';
|
|
23
|
-
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
24
237
|
import { dataSourceOptions } from '@zola_do/typeorm';
|
|
25
238
|
|
|
239
|
+
// Full options structure
|
|
240
|
+
{
|
|
241
|
+
type: 'postgres',
|
|
242
|
+
host: 'localhost',
|
|
243
|
+
port: 5432,
|
|
244
|
+
database: 'myapp',
|
|
245
|
+
username: 'postgres',
|
|
246
|
+
password: 'secret',
|
|
247
|
+
entities: ['dist/**/*.entity.js', '**/*.entity.js'],
|
|
248
|
+
migrations: ['dist/**/*.migration.js', '**/*.migration.js'],
|
|
249
|
+
synchronize: false, // Use migrations in production
|
|
250
|
+
logging: process.env.NODE_ENV === 'development',
|
|
251
|
+
// ... more TypeORM options
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Production Configuration
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
// app.module.ts
|
|
259
|
+
import { dataSourceOptions } from "@zola_do/typeorm";
|
|
260
|
+
|
|
26
261
|
@Module({
|
|
27
262
|
imports: [
|
|
28
|
-
TypeOrmModule.forRoot(
|
|
29
|
-
|
|
263
|
+
TypeOrmModule.forRoot({
|
|
264
|
+
...dataSourceOptions,
|
|
265
|
+
synchronize: false,
|
|
266
|
+
migrationsRun: true,
|
|
267
|
+
logging: ["error", "warn"],
|
|
268
|
+
}),
|
|
30
269
|
],
|
|
31
270
|
})
|
|
32
271
|
export class AppModule {}
|
|
33
272
|
```
|
|
34
273
|
|
|
35
|
-
|
|
274
|
+
## Environment Variables
|
|
275
|
+
|
|
276
|
+
| Variable | Description | Default |
|
|
277
|
+
| ------------------- | ---------------------------- | ------------------------ |
|
|
278
|
+
| `DATABASE_HOST` | Database host | `localhost` |
|
|
279
|
+
| `DATABASE_PORT` | Database port | `5432` |
|
|
280
|
+
| `DATABASE_NAME` | Database name | Falls back to `APP_NAME` |
|
|
281
|
+
| `DATABASE_USER` | Database user | `postgres` |
|
|
282
|
+
| `DATABASE_PASSWORD` | Database password | Required in production |
|
|
283
|
+
| `APP_NAME` | Fallback for `DATABASE_NAME` | — |
|
|
284
|
+
|
|
285
|
+
### Validation Rules
|
|
286
|
+
|
|
287
|
+
- In production (`NODE_ENV === 'production'`), `DATABASE_PASSWORD` is required
|
|
288
|
+
- Either `DATABASE_NAME` or `APP_NAME` must be set
|
|
289
|
+
|
|
290
|
+
## Auto-Discovery
|
|
291
|
+
|
|
292
|
+
Entities and migrations are auto-discovered:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// dataSourceOptions.entities supports glob patterns
|
|
296
|
+
entities: [
|
|
297
|
+
__dirname + "/../../**/*.entity.js", // Compiled output
|
|
298
|
+
"**/*.entity.js", // Source files
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
// dataSourceOptions.migrations supports glob patterns
|
|
302
|
+
migrations: [__dirname + "/../../**/*.migration.js"];
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Entity File Location
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
src/
|
|
309
|
+
├── entities/
|
|
310
|
+
│ ├── base.entity.ts
|
|
311
|
+
│ ├── product.entity.ts
|
|
312
|
+
│ └── category.entity.ts
|
|
313
|
+
└── app.module.ts
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Migrations
|
|
317
|
+
|
|
318
|
+
### Generate Migration
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
npx typeorm migration:generate -d dist/typeorm/data-source.js src/migrations/ProductColumns
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Run Migrations
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
npx typeorm migration:run -d dist/typeorm/data-source.js
|
|
328
|
+
```
|
|
36
329
|
|
|
37
|
-
|
|
330
|
+
### Create Migration Manually
|
|
38
331
|
|
|
39
332
|
```typescript
|
|
40
|
-
import {
|
|
333
|
+
import { MigrationInterface, QueryRunner, Table } from "typeorm";
|
|
41
334
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
335
|
+
export class CreateProducts1704064000000 implements MigrationInterface {
|
|
336
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
337
|
+
await queryRunner.createTable(
|
|
338
|
+
new Table({
|
|
339
|
+
name: "products",
|
|
340
|
+
columns: [
|
|
341
|
+
{
|
|
342
|
+
name: "id",
|
|
343
|
+
type: "uuid",
|
|
344
|
+
isPrimary: true,
|
|
345
|
+
generationStrategy: "uuid",
|
|
346
|
+
},
|
|
347
|
+
{ name: "name", type: "varchar" },
|
|
348
|
+
{ name: "price", type: "decimal", precision: 10, scale: 2 },
|
|
349
|
+
{ name: "created_at", type: "timestamp", default: "now()" },
|
|
350
|
+
{ name: "updated_at", type: "timestamp", default: "now()" },
|
|
351
|
+
],
|
|
352
|
+
}),
|
|
353
|
+
true,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
358
|
+
await queryRunner.dropTable("products");
|
|
359
|
+
}
|
|
360
|
+
}
|
|
47
361
|
```
|
|
48
362
|
|
|
49
|
-
|
|
363
|
+
## API Reference
|
|
50
364
|
|
|
51
|
-
|
|
365
|
+
### Constants
|
|
52
366
|
|
|
53
367
|
```typescript
|
|
54
|
-
|
|
368
|
+
TypeOrmConfigHelper.DATABASE_HOST;
|
|
369
|
+
TypeOrmConfigHelper.DATABASE_PORT;
|
|
370
|
+
TypeOrmConfigHelper.DATABASE_NAME;
|
|
371
|
+
TypeOrmConfigHelper.DATABASE_USER;
|
|
372
|
+
TypeOrmConfigHelper.DATABASE_PASSWORD;
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### DataSource
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { dataSourceOptions } from "@zola_do/typeorm";
|
|
379
|
+
// Type: DataSourceOptions
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Service
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
import { TypeOrmService } from "@zola_do/typeorm";
|
|
55
386
|
|
|
56
387
|
@Injectable()
|
|
57
|
-
|
|
388
|
+
class MyService {
|
|
58
389
|
constructor(private readonly typeorm: TypeOrmService) {}
|
|
390
|
+
|
|
391
|
+
async query(sql: string, parameters?: any[]) {
|
|
392
|
+
return this.typeorm.query(sql, parameters);
|
|
393
|
+
}
|
|
59
394
|
}
|
|
60
395
|
```
|
|
61
396
|
|
|
62
|
-
##
|
|
397
|
+
## Troubleshooting
|
|
398
|
+
|
|
399
|
+
### Q: Entities not being loaded?
|
|
400
|
+
|
|
401
|
+
Check glob pattern in `dataSourceOptions.entities`. The pattern should match your compiled `.js` files:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
entities: [__dirname + "/../../**/*.entity.js"];
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Q: Migrations not running?
|
|
408
|
+
|
|
409
|
+
Ensure `migrationsRun: true` in production or run manually:
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
npm run migration:run
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Q: Connection refused?
|
|
416
|
+
|
|
417
|
+
Verify `DATABASE_HOST` and `DATABASE_PORT` are correct and the database is accessible.
|
|
418
|
+
|
|
419
|
+
## Related Packages
|
|
420
|
+
|
|
421
|
+
- [@zola_do/crud](../crud) — Uses TypeORM entities
|
|
422
|
+
- [@zola_do/collection-query](../collection-query) — Query builder for TypeORM
|
|
63
423
|
|
|
64
|
-
|
|
65
|
-
|----------|-------------|---------|
|
|
66
|
-
| `DATABASE_HOST` | Database host | `localhost` |
|
|
67
|
-
| `DATABASE_PORT` | Database port | `5432` |
|
|
68
|
-
| `DATABASE_NAME` | Database name | Falls back to `APP_NAME` |
|
|
69
|
-
| `DATABASE_USER` | Database user | `postgres` |
|
|
70
|
-
| `DATABASE_PASSWORD` | Database password | Required in production |
|
|
71
|
-
| `APP_NAME` | Fallback for `DATABASE_NAME` when `DATABASE_NAME` is not set | — |
|
|
424
|
+
## License
|
|
72
425
|
|
|
73
|
-
|
|
426
|
+
ISC
|
|
74
427
|
|
|
75
428
|
## Community
|
|
76
429
|
|