@ciscode/database-kit 1.0.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/CHANGELOG.md +206 -0
- package/LICENSE +21 -0
- package/README.md +446 -0
- package/dist/adapters/mongo.adapter.d.ts +44 -0
- package/dist/adapters/mongo.adapter.d.ts.map +1 -0
- package/dist/adapters/mongo.adapter.js +146 -0
- package/dist/adapters/mongo.adapter.js.map +1 -0
- package/dist/adapters/postgres.adapter.d.ts +49 -0
- package/dist/adapters/postgres.adapter.d.ts.map +1 -0
- package/dist/adapters/postgres.adapter.js +229 -0
- package/dist/adapters/postgres.adapter.js.map +1 -0
- package/dist/config/database.config.d.ts +55 -0
- package/dist/config/database.config.d.ts.map +1 -0
- package/dist/config/database.config.js +122 -0
- package/dist/config/database.config.js.map +1 -0
- package/dist/config/database.constants.d.ts +41 -0
- package/dist/config/database.constants.d.ts.map +1 -0
- package/dist/config/database.constants.js +45 -0
- package/dist/config/database.constants.js.map +1 -0
- package/dist/contracts/database.contracts.d.ts +191 -0
- package/dist/contracts/database.contracts.d.ts.map +1 -0
- package/dist/contracts/database.contracts.js +21 -0
- package/dist/contracts/database.contracts.js.map +1 -0
- package/dist/database-kit.module.d.ts +71 -0
- package/dist/database-kit.module.d.ts.map +1 -0
- package/dist/database-kit.module.js +158 -0
- package/dist/database-kit.module.js.map +1 -0
- package/dist/filters/database-exception.filter.d.ts +51 -0
- package/dist/filters/database-exception.filter.d.ts.map +1 -0
- package/dist/filters/database-exception.filter.js +236 -0
- package/dist/filters/database-exception.filter.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/database.decorators.d.ts +31 -0
- package/dist/middleware/database.decorators.d.ts.map +1 -0
- package/dist/middleware/database.decorators.js +39 -0
- package/dist/middleware/database.decorators.js.map +1 -0
- package/dist/services/database.service.d.ts +99 -0
- package/dist/services/database.service.d.ts.map +1 -0
- package/dist/services/database.service.js +205 -0
- package/dist/services/database.service.js.map +1 -0
- package/dist/services/logger.service.d.ts +63 -0
- package/dist/services/logger.service.d.ts.map +1 -0
- package/dist/services/logger.service.js +93 -0
- package/dist/services/logger.service.js.map +1 -0
- package/dist/utils/pagination.utils.d.ts +57 -0
- package/dist/utils/pagination.utils.d.ts.map +1 -0
- package/dist/utils/pagination.utils.js +113 -0
- package/dist/utils/pagination.utils.js.map +1 -0
- package/dist/utils/validation.utils.d.ts +60 -0
- package/dist/utils/validation.utils.d.ts.map +1 -0
- package/dist/utils/validation.utils.js +117 -0
- package/dist/utils/validation.utils.js.map +1 -0
- package/package.json +83 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
### Planned
|
|
13
|
+
|
|
14
|
+
- MySQL adapter support
|
|
15
|
+
- Redis caching layer
|
|
16
|
+
- Query builder interface
|
|
17
|
+
- Soft delete built-in support
|
|
18
|
+
- Audit logging
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [1.0.0] - 2026-01-31
|
|
23
|
+
|
|
24
|
+
### ๐ Initial Release
|
|
25
|
+
|
|
26
|
+
Complete refactoring following CISCODE AuthKit patterns and best practices.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
#### Core Features
|
|
31
|
+
|
|
32
|
+
- **Unified Repository API** - Same interface for MongoDB and PostgreSQL
|
|
33
|
+
- `create(data)` - Create new records
|
|
34
|
+
- `findById(id)` - Find by primary key
|
|
35
|
+
- `findAll(filter)` - Find all matching records
|
|
36
|
+
- `findPage(options)` - Paginated queries
|
|
37
|
+
- `updateById(id, data)` - Update by primary key
|
|
38
|
+
- `deleteById(id)` - Delete by primary key
|
|
39
|
+
- `count(filter)` - Count matching records
|
|
40
|
+
- `exists(filter)` - Check if records exist
|
|
41
|
+
|
|
42
|
+
#### NestJS Integration
|
|
43
|
+
|
|
44
|
+
- **DatabaseKitModule** - NestJS module with DI support
|
|
45
|
+
- `forRoot(options)` - Synchronous configuration
|
|
46
|
+
- `forRootAsync(options)` - Async configuration with factory
|
|
47
|
+
- `forFeature(token, config)` - Multiple database connections
|
|
48
|
+
|
|
49
|
+
#### Database Adapters
|
|
50
|
+
|
|
51
|
+
- **MongoAdapter** - MongoDB support via Mongoose
|
|
52
|
+
- Connection pooling
|
|
53
|
+
- Auto-reconnection
|
|
54
|
+
- Lean queries by default
|
|
55
|
+
- **PostgresAdapter** - PostgreSQL support via Knex
|
|
56
|
+
- Connection pooling
|
|
57
|
+
- Advanced filter operators (gt, gte, lt, lte, in, nin, like, etc.)
|
|
58
|
+
- Column whitelisting for security
|
|
59
|
+
|
|
60
|
+
#### Services
|
|
61
|
+
|
|
62
|
+
- **DatabaseService** - Main facade for database operations
|
|
63
|
+
- Unified interface for both databases
|
|
64
|
+
- Connection lifecycle management
|
|
65
|
+
- Repository factory methods
|
|
66
|
+
- **LoggerService** - Structured logging
|
|
67
|
+
- NestJS Logger integration
|
|
68
|
+
- Context-aware logging
|
|
69
|
+
|
|
70
|
+
#### Middleware
|
|
71
|
+
|
|
72
|
+
- **@InjectDatabase()** - Decorator for injecting DatabaseService
|
|
73
|
+
- **@InjectDatabaseByToken(token)** - Decorator for named connections
|
|
74
|
+
|
|
75
|
+
#### Filters
|
|
76
|
+
|
|
77
|
+
- **DatabaseExceptionFilter** - Global exception handling
|
|
78
|
+
- MongoDB error parsing
|
|
79
|
+
- PostgreSQL error parsing
|
|
80
|
+
- Consistent error response format
|
|
81
|
+
|
|
82
|
+
#### Utilities
|
|
83
|
+
|
|
84
|
+
- Pagination helpers (`normalizePaginationOptions`, `calculatePagination`)
|
|
85
|
+
- Validation helpers (`isValidMongoId`, `isValidUuid`, `sanitizeFilter`)
|
|
86
|
+
|
|
87
|
+
#### Configuration
|
|
88
|
+
|
|
89
|
+
- Environment-driven configuration
|
|
90
|
+
- Config validation with fail-fast errors
|
|
91
|
+
- Connection string validation
|
|
92
|
+
|
|
93
|
+
#### Documentation
|
|
94
|
+
|
|
95
|
+
- Comprehensive README with examples
|
|
96
|
+
- SECURITY.md with vulnerability reporting guidelines
|
|
97
|
+
- TROUBLESHOOTING.md with common issues
|
|
98
|
+
- CONTRIBUTING.md with development guidelines
|
|
99
|
+
- copilot-instructions.md for AI-assisted development
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
#### Architecture Improvements
|
|
104
|
+
|
|
105
|
+
- Restructured to follow AuthKit patterns
|
|
106
|
+
- Separated concerns into adapters, services, and contracts
|
|
107
|
+
- Moved from `core/` structure to proper layer separation
|
|
108
|
+
|
|
109
|
+
#### Naming Conventions
|
|
110
|
+
|
|
111
|
+
- All files now use kebab-case with suffixes
|
|
112
|
+
- Consistent class naming (PascalCase)
|
|
113
|
+
- Consistent function naming (camelCase)
|
|
114
|
+
|
|
115
|
+
#### TypeScript Improvements
|
|
116
|
+
|
|
117
|
+
- Added path aliases for clean imports
|
|
118
|
+
- Stricter TypeScript configuration
|
|
119
|
+
- Full type coverage with no implicit any
|
|
120
|
+
|
|
121
|
+
### Fixed
|
|
122
|
+
|
|
123
|
+
- N/A (initial release)
|
|
124
|
+
|
|
125
|
+
### Security
|
|
126
|
+
|
|
127
|
+
- Column whitelisting for PostgreSQL repositories
|
|
128
|
+
- Parameterized queries to prevent injection
|
|
129
|
+
- Error sanitization in exception filter
|
|
130
|
+
- No credentials in error messages
|
|
131
|
+
|
|
132
|
+
### Breaking Changes
|
|
133
|
+
|
|
134
|
+
- N/A (initial release)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Pre-1.0 History
|
|
139
|
+
|
|
140
|
+
### [0.x.x] - Development
|
|
141
|
+
|
|
142
|
+
- Initial development and prototyping
|
|
143
|
+
- Basic MongoDB and PostgreSQL adapters
|
|
144
|
+
- Simple NestJS module integration
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Migration Guide
|
|
149
|
+
|
|
150
|
+
### From Pre-1.0 to 1.0.0
|
|
151
|
+
|
|
152
|
+
If you were using a pre-release version, follow these steps:
|
|
153
|
+
|
|
154
|
+
1. **Update imports:**
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Before
|
|
158
|
+
import { Database } from "@ciscode/database-kit/core/database";
|
|
159
|
+
|
|
160
|
+
// After
|
|
161
|
+
import { DatabaseService } from "@ciscode/database-kit";
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
2. **Update module configuration:**
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
// Before
|
|
168
|
+
DatabaseModule.forRoot(config);
|
|
169
|
+
|
|
170
|
+
// After
|
|
171
|
+
DatabaseKitModule.forRoot({ config });
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
3. **Update decorator usage:**
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// Before
|
|
178
|
+
@InjectDatabase() private db: Database
|
|
179
|
+
|
|
180
|
+
// After
|
|
181
|
+
@InjectDatabase() private db: DatabaseService
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
4. **Update repository creation:**
|
|
185
|
+
```typescript
|
|
186
|
+
// Methods remain the same
|
|
187
|
+
const repo = db.createMongoRepository<User>({ model: UserModel });
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Release Schedule
|
|
193
|
+
|
|
194
|
+
We follow semantic versioning:
|
|
195
|
+
|
|
196
|
+
- **Patch releases (x.x.X)**: Bug fixes, released as needed
|
|
197
|
+
- **Minor releases (x.X.0)**: New features, ~monthly
|
|
198
|
+
- **Major releases (X.0.0)**: Breaking changes, ~annually
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Links
|
|
203
|
+
|
|
204
|
+
- [GitHub Releases](https://github.com/CISCODE-MA/DatabaseKit/releases)
|
|
205
|
+
- [npm Package](https://www.npmjs.com/package/@ciscode/database-kit)
|
|
206
|
+
- [Documentation](https://github.com/CISCODE-MA/DatabaseKit#readme)
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 **Ciscode**
|
|
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,446 @@
|
|
|
1
|
+
# @ciscode/database-kit
|
|
2
|
+
|
|
3
|
+
A NestJS-friendly, OOP-style database library providing a unified repository API for **MongoDB** and **PostgreSQL**.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@ciscode/database-kit)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## โจ Features
|
|
12
|
+
|
|
13
|
+
- โ
**Unified Repository API** - Same interface for MongoDB and PostgreSQL
|
|
14
|
+
- โ
**NestJS Integration** - First-class support with `DatabaseKitModule`
|
|
15
|
+
- โ
**TypeScript First** - Full type safety and IntelliSense
|
|
16
|
+
- โ
**Pagination Built-in** - Consistent pagination across databases
|
|
17
|
+
- โ
**Environment-Driven** - Zero hardcoding, all config from env vars
|
|
18
|
+
- โ
**Exception Handling** - Global exception filter for database errors
|
|
19
|
+
- โ
**Clean Architecture** - Follows CISCODE standards and best practices
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## ๐ฆ Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @ciscode/database-kit
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Peer Dependencies
|
|
30
|
+
|
|
31
|
+
Make sure you have NestJS installed:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @nestjs/common @nestjs/core reflect-metadata
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Database Drivers
|
|
38
|
+
|
|
39
|
+
Install the driver for your database:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# For MongoDB
|
|
43
|
+
npm install mongoose
|
|
44
|
+
|
|
45
|
+
# For PostgreSQL
|
|
46
|
+
npm install pg
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ๐ Quick Start
|
|
52
|
+
|
|
53
|
+
### 1. Import the Module
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// app.module.ts
|
|
57
|
+
import { Module } from "@nestjs/common";
|
|
58
|
+
import { DatabaseKitModule } from "@ciscode/database-kit";
|
|
59
|
+
|
|
60
|
+
@Module({
|
|
61
|
+
imports: [
|
|
62
|
+
DatabaseKitModule.forRoot({
|
|
63
|
+
config: {
|
|
64
|
+
type: "mongo", // or 'postgres'
|
|
65
|
+
connectionString: process.env.MONGO_URI!,
|
|
66
|
+
},
|
|
67
|
+
}),
|
|
68
|
+
],
|
|
69
|
+
})
|
|
70
|
+
export class AppModule {}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Inject and Use
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// users.service.ts
|
|
77
|
+
import { Injectable } from "@nestjs/common";
|
|
78
|
+
import {
|
|
79
|
+
InjectDatabase,
|
|
80
|
+
DatabaseService,
|
|
81
|
+
Repository,
|
|
82
|
+
} from "@ciscode/database-kit";
|
|
83
|
+
import { UserModel } from "./user.model";
|
|
84
|
+
|
|
85
|
+
interface User {
|
|
86
|
+
_id: string;
|
|
87
|
+
name: string;
|
|
88
|
+
email: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Injectable()
|
|
92
|
+
export class UsersService {
|
|
93
|
+
private readonly usersRepo: Repository<User>;
|
|
94
|
+
|
|
95
|
+
constructor(@InjectDatabase() private readonly db: DatabaseService) {
|
|
96
|
+
this.usersRepo = db.createMongoRepository<User>({ model: UserModel });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async createUser(data: Partial<User>): Promise<User> {
|
|
100
|
+
return this.usersRepo.create(data);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getUser(id: string): Promise<User | null> {
|
|
104
|
+
return this.usersRepo.findById(id);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async listUsers(page = 1, limit = 10) {
|
|
108
|
+
return this.usersRepo.findPage({ page, limit });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## โ๏ธ Configuration
|
|
116
|
+
|
|
117
|
+
### Environment Variables
|
|
118
|
+
|
|
119
|
+
| Variable | Description | Required |
|
|
120
|
+
| ----------------------------- | ---------------------------------------- | -------------- |
|
|
121
|
+
| `DATABASE_TYPE` | Database type (`mongo` or `postgres`) | Yes |
|
|
122
|
+
| `MONGO_URI` | MongoDB connection string | For MongoDB |
|
|
123
|
+
| `DATABASE_URL` | PostgreSQL connection string | For PostgreSQL |
|
|
124
|
+
| `DATABASE_POOL_SIZE` | Connection pool size (default: 10) | No |
|
|
125
|
+
| `DATABASE_CONNECTION_TIMEOUT` | Connection timeout in ms (default: 5000) | No |
|
|
126
|
+
|
|
127
|
+
### Synchronous Configuration
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
DatabaseKitModule.forRoot({
|
|
131
|
+
config: {
|
|
132
|
+
type: "postgres",
|
|
133
|
+
connectionString: "postgresql://user:pass@localhost:5432/mydb",
|
|
134
|
+
},
|
|
135
|
+
autoConnect: true, // default: true
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Async Configuration (Recommended)
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { ConfigModule, ConfigService } from "@nestjs/config";
|
|
143
|
+
|
|
144
|
+
DatabaseKitModule.forRootAsync({
|
|
145
|
+
imports: [ConfigModule],
|
|
146
|
+
useFactory: (config: ConfigService) => ({
|
|
147
|
+
config: {
|
|
148
|
+
type: config.get("DATABASE_TYPE") as "mongo" | "postgres",
|
|
149
|
+
connectionString: config.get("DATABASE_URL")!,
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
inject: [ConfigService],
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Multiple Databases
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
@Module({
|
|
160
|
+
imports: [
|
|
161
|
+
// Primary database
|
|
162
|
+
DatabaseKitModule.forRoot({
|
|
163
|
+
config: { type: "mongo", connectionString: process.env.MONGO_URI! },
|
|
164
|
+
}),
|
|
165
|
+
// Analytics database
|
|
166
|
+
DatabaseKitModule.forFeature("ANALYTICS_DB", {
|
|
167
|
+
type: "postgres",
|
|
168
|
+
connectionString: process.env.ANALYTICS_DB_URL!,
|
|
169
|
+
}),
|
|
170
|
+
],
|
|
171
|
+
})
|
|
172
|
+
export class AppModule {}
|
|
173
|
+
|
|
174
|
+
// Usage
|
|
175
|
+
@Injectable()
|
|
176
|
+
export class AnalyticsService {
|
|
177
|
+
constructor(
|
|
178
|
+
@InjectDatabaseByToken("ANALYTICS_DB")
|
|
179
|
+
private readonly analyticsDb: DatabaseService,
|
|
180
|
+
) {}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## ๐ Repository API
|
|
187
|
+
|
|
188
|
+
Both MongoDB and PostgreSQL repositories expose the same interface:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
interface Repository<T> {
|
|
192
|
+
create(data: Partial<T>): Promise<T>;
|
|
193
|
+
findById(id: string | number): Promise<T | null>;
|
|
194
|
+
findAll(filter?: Filter): Promise<T[]>;
|
|
195
|
+
findPage(options?: PageOptions): Promise<PageResult<T>>;
|
|
196
|
+
updateById(id: string | number, update: Partial<T>): Promise<T | null>;
|
|
197
|
+
deleteById(id: string | number): Promise<boolean>;
|
|
198
|
+
count(filter?: Filter): Promise<number>;
|
|
199
|
+
exists(filter?: Filter): Promise<boolean>;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Pagination
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const result = await repo.findPage({
|
|
207
|
+
filter: { status: "active" },
|
|
208
|
+
page: 1,
|
|
209
|
+
limit: 20,
|
|
210
|
+
sort: "-createdAt", // or { createdAt: -1 }
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Result:
|
|
214
|
+
// {
|
|
215
|
+
// data: [...],
|
|
216
|
+
// page: 1,
|
|
217
|
+
// limit: 20,
|
|
218
|
+
// total: 150,
|
|
219
|
+
// pages: 8
|
|
220
|
+
// }
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### MongoDB Repository
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { Model } from "mongoose";
|
|
227
|
+
|
|
228
|
+
// Create your Mongoose model as usual
|
|
229
|
+
const usersRepo = db.createMongoRepository<User>({ model: UserModel });
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### PostgreSQL Repository
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
interface Order {
|
|
236
|
+
id: string;
|
|
237
|
+
user_id: string;
|
|
238
|
+
total: number;
|
|
239
|
+
created_at: Date;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const ordersRepo = db.createPostgresRepository<Order>({
|
|
243
|
+
table: "orders",
|
|
244
|
+
primaryKey: "id",
|
|
245
|
+
columns: ["id", "user_id", "total", "created_at", "is_deleted"],
|
|
246
|
+
defaultFilter: { is_deleted: false }, // Soft delete support
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### PostgreSQL Advanced Filters
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// Comparison operators
|
|
254
|
+
await repo.findAll({
|
|
255
|
+
price: { gt: 100, lte: 500 },
|
|
256
|
+
status: { ne: "cancelled" },
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// IN / NOT IN
|
|
260
|
+
await repo.findAll({
|
|
261
|
+
category: { in: ["electronics", "books"] },
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// LIKE (case-insensitive)
|
|
265
|
+
await repo.findAll({
|
|
266
|
+
name: { like: "%widget%" },
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// NULL checks
|
|
270
|
+
await repo.findAll({
|
|
271
|
+
deleted_at: { isNull: true },
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## ๐ก๏ธ Error Handling
|
|
278
|
+
|
|
279
|
+
### Global Exception Filter
|
|
280
|
+
|
|
281
|
+
Register the filter globally to catch and format database errors:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// main.ts
|
|
285
|
+
import { DatabaseExceptionFilter } from "@ciscode/database-kit";
|
|
286
|
+
|
|
287
|
+
app.useGlobalFilters(new DatabaseExceptionFilter());
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Or in a module:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { APP_FILTER } from "@nestjs/core";
|
|
294
|
+
import { DatabaseExceptionFilter } from "@ciscode/database-kit";
|
|
295
|
+
|
|
296
|
+
@Module({
|
|
297
|
+
providers: [{ provide: APP_FILTER, useClass: DatabaseExceptionFilter }],
|
|
298
|
+
})
|
|
299
|
+
export class AppModule {}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Error Response Format
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"statusCode": 409,
|
|
307
|
+
"message": "A record with this value already exists",
|
|
308
|
+
"error": "DuplicateKeyError",
|
|
309
|
+
"timestamp": "2026-01-31T12:00:00.000Z",
|
|
310
|
+
"path": "/api/users"
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## ๐ง Utilities
|
|
317
|
+
|
|
318
|
+
### Pagination Utilities
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import {
|
|
322
|
+
normalizePaginationOptions,
|
|
323
|
+
parseSortString,
|
|
324
|
+
calculateOffset,
|
|
325
|
+
} from "@ciscode/database-kit";
|
|
326
|
+
|
|
327
|
+
const normalized = normalizePaginationOptions({ page: 1 });
|
|
328
|
+
const sortObj = parseSortString("-createdAt,name");
|
|
329
|
+
const offset = calculateOffset(2, 10); // 10
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Validation Utilities
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import {
|
|
336
|
+
isValidMongoId,
|
|
337
|
+
isValidUuid,
|
|
338
|
+
sanitizeFilter,
|
|
339
|
+
pickFields,
|
|
340
|
+
} from "@ciscode/database-kit";
|
|
341
|
+
|
|
342
|
+
isValidMongoId("507f1f77bcf86cd799439011"); // true
|
|
343
|
+
isValidUuid("550e8400-e29b-41d4-a716-446655440000"); // true
|
|
344
|
+
|
|
345
|
+
const clean = sanitizeFilter({ name: "John", age: undefined });
|
|
346
|
+
// { name: 'John' }
|
|
347
|
+
|
|
348
|
+
const picked = pickFields(data, ["name", "email"]);
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## ๐งช Testing
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
# Run tests
|
|
357
|
+
npm test
|
|
358
|
+
|
|
359
|
+
# Run with coverage
|
|
360
|
+
npm run test:cov
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Mocking in Tests
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
const mockDb = {
|
|
367
|
+
createMongoRepository: jest.fn().mockReturnValue({
|
|
368
|
+
create: jest.fn(),
|
|
369
|
+
findById: jest.fn(),
|
|
370
|
+
// ...
|
|
371
|
+
}),
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const module = await Test.createTestingModule({
|
|
375
|
+
providers: [UsersService, { provide: DATABASE_TOKEN, useValue: mockDb }],
|
|
376
|
+
}).compile();
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## ๐ Project Structure
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
src/
|
|
385
|
+
โโโ index.ts # Public API exports
|
|
386
|
+
โโโ database-kit.module.ts # NestJS module
|
|
387
|
+
โโโ adapters/ # Database adapters
|
|
388
|
+
โ โโโ mongo.adapter.ts
|
|
389
|
+
โ โโโ postgres.adapter.ts
|
|
390
|
+
โโโ config/ # Configuration
|
|
391
|
+
โ โโโ database.config.ts
|
|
392
|
+
โ โโโ database.constants.ts
|
|
393
|
+
โโโ contracts/ # TypeScript contracts
|
|
394
|
+
โ โโโ database.contracts.ts
|
|
395
|
+
โโโ filters/ # Exception filters
|
|
396
|
+
โ โโโ database-exception.filter.ts
|
|
397
|
+
โโโ middleware/ # Decorators
|
|
398
|
+
โ โโโ database.decorators.ts
|
|
399
|
+
โโโ services/ # Business logic
|
|
400
|
+
โ โโโ database.service.ts
|
|
401
|
+
โ โโโ logger.service.ts
|
|
402
|
+
โโโ utils/ # Utilities
|
|
403
|
+
โโโ pagination.utils.ts
|
|
404
|
+
โโโ validation.utils.ts
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## ๐ Security
|
|
410
|
+
|
|
411
|
+
See [SECURITY.md](SECURITY.md) for:
|
|
412
|
+
|
|
413
|
+
- Vulnerability reporting
|
|
414
|
+
- Security best practices
|
|
415
|
+
- Security checklist
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## ๐ค Contributing
|
|
420
|
+
|
|
421
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for:
|
|
422
|
+
|
|
423
|
+
- Development setup
|
|
424
|
+
- Git workflow
|
|
425
|
+
- Code standards
|
|
426
|
+
- PR process
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## ๐ Changelog
|
|
431
|
+
|
|
432
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history.
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## ๐ License
|
|
437
|
+
|
|
438
|
+
MIT ยฉ [C International Service](https://ciscode.com)
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## ๐ Support
|
|
443
|
+
|
|
444
|
+
- ๐ง Email: info@ciscode.com
|
|
445
|
+
- ๐ Issues: [GitHub Issues](https://github.com/CISCODE-MA/DatabaseKit/issues)
|
|
446
|
+
- ๐ Docs: [GitHub Wiki](https://github.com/CISCODE-MA/DatabaseKit/wiki)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import mongoose, { ConnectOptions } from 'mongoose';
|
|
2
|
+
import { MongoDatabaseConfig, MongoRepositoryOptions, Repository } from '../contracts/database.contracts';
|
|
3
|
+
/**
|
|
4
|
+
* MongoDB adapter for DatabaseKit.
|
|
5
|
+
* Handles MongoDB connection and repository creation via Mongoose.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const adapter = new MongoAdapter({ type: 'mongo', connectionString: 'mongodb://...' });
|
|
10
|
+
* await adapter.connect();
|
|
11
|
+
* const repo = adapter.createRepository({ model: UserModel });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare class MongoAdapter {
|
|
15
|
+
private readonly logger;
|
|
16
|
+
private readonly config;
|
|
17
|
+
private connectionPromise?;
|
|
18
|
+
constructor(config: MongoDatabaseConfig);
|
|
19
|
+
/**
|
|
20
|
+
* Establishes connection to MongoDB.
|
|
21
|
+
* Connection is lazy-loaded and cached for reuse.
|
|
22
|
+
*
|
|
23
|
+
* @param options - Additional Mongoose connection options
|
|
24
|
+
* @returns Promise resolving to mongoose instance
|
|
25
|
+
*/
|
|
26
|
+
connect(options?: ConnectOptions): Promise<typeof mongoose>;
|
|
27
|
+
/**
|
|
28
|
+
* Gracefully disconnects from MongoDB.
|
|
29
|
+
*/
|
|
30
|
+
disconnect(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Checks if connected to MongoDB.
|
|
33
|
+
*/
|
|
34
|
+
isConnected(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a repository for a Mongoose model.
|
|
37
|
+
* The repository provides a standardized CRUD interface.
|
|
38
|
+
*
|
|
39
|
+
* @param opts - Options containing the Mongoose model
|
|
40
|
+
* @returns Repository instance with CRUD methods
|
|
41
|
+
*/
|
|
42
|
+
createRepository<T = unknown>(opts: MongoRepositoryOptions): Repository<T>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=mongo.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongo.adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/mongo.adapter.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,EAAE,EAAE,cAAc,EAAS,MAAM,UAAU,CAAC;AAE3D,OAAO,EACH,mBAAmB,EACnB,sBAAsB,EACtB,UAAU,EAGb,MAAM,iCAAiC,CAAC;AAEzC;;;;;;;;;;GAUG;AACH,qBACa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,iBAAiB,CAAC,CAA2B;gBAEzC,MAAM,EAAE,mBAAmB;IAKvC;;;;;;OAMG;IACG,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,QAAQ,CAAC;IA0BrE;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;;;;;OAMG;IACH,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,sBAAsB,GAAG,UAAU,CAAC,CAAC,CAAC;CAwE7E"}
|