@elsikora/nestjs-typeorm-aws-connector 1.3.1 → 1.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.
- package/README.md +344 -235
- package/dist/cjs/external/tslib/tslib.es6.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/modules/typeorm-aws-connector/rotator/rotator.service.d.ts +3 -3
- package/dist/cjs/modules/typeorm-aws-connector/rotator/rotator.service.js +39 -31
- package/dist/cjs/modules/typeorm-aws-connector/rotator/rotator.service.js.map +1 -1
- package/dist/cjs/modules/typeorm-aws-connector/typeorm-aws-connector.module.d.ts +1 -1
- package/dist/cjs/modules/typeorm-aws-connector/typeorm-aws-connector.module.js +7 -25
- package/dist/cjs/modules/typeorm-aws-connector/typeorm-aws-connector.module.js.map +1 -1
- package/dist/cjs/modules/typeorm-aws-connector/typeorm-aws-connector.service.d.ts +25 -3
- package/dist/cjs/modules/typeorm-aws-connector/typeorm-aws-connector.service.js +246 -30
- package/dist/cjs/modules/typeorm-aws-connector/typeorm-aws-connector.service.js.map +1 -1
- package/dist/cjs/package.json +4 -4
- package/dist/cjs/shared/constant/typeorm-aws-connector/constant.d.ts +7 -0
- package/dist/cjs/shared/constant/typeorm-aws-connector/constant.js +70 -2
- package/dist/cjs/shared/constant/typeorm-aws-connector/constant.js.map +1 -1
- package/dist/cjs/shared/enum/database/type.enum.js.map +1 -1
- package/dist/cjs/shared/enum/relation-load-strategy.enum.js.map +1 -1
- package/dist/cjs/shared/interface/aws/database-credentials-secret.interface.d.ts +4 -0
- package/dist/cjs/shared/interface/aws/index.d.ts +1 -0
- package/dist/cjs/shared/interface/typeorm-aws-connector/config.interface.d.ts +11 -12
- package/dist/cjs/shared/interface/typeorm-aws-connector/index.d.ts +3 -0
- package/dist/cjs/shared/interface/typeorm-aws-connector/module-async-options.interface.d.ts +4 -4
- package/dist/cjs/shared/interface/typeorm-aws-connector/ssm-lookup-defaults.interface.d.ts +2 -0
- package/dist/cjs/shared/interface/typeorm-aws-connector/ssm-lookups.interface.d.ts +16 -0
- package/dist/cjs/shared/interface/typeorm-aws-connector/structured-lookup.interface.d.ts +2 -0
- package/dist/cjs/shared/type/database/config-rotation.type.d.ts +1 -3
- package/dist/esm/external/tslib/tslib.es6.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/modules/typeorm-aws-connector/rotator/rotator.service.d.ts +3 -3
- package/dist/esm/modules/typeorm-aws-connector/rotator/rotator.service.js +40 -32
- package/dist/esm/modules/typeorm-aws-connector/rotator/rotator.service.js.map +1 -1
- package/dist/esm/modules/typeorm-aws-connector/typeorm-aws-connector.module.d.ts +1 -1
- package/dist/esm/modules/typeorm-aws-connector/typeorm-aws-connector.module.js +7 -25
- package/dist/esm/modules/typeorm-aws-connector/typeorm-aws-connector.module.js.map +1 -1
- package/dist/esm/modules/typeorm-aws-connector/typeorm-aws-connector.service.d.ts +25 -3
- package/dist/esm/modules/typeorm-aws-connector/typeorm-aws-connector.service.js +248 -32
- package/dist/esm/modules/typeorm-aws-connector/typeorm-aws-connector.service.js.map +1 -1
- package/dist/esm/package.json +4 -4
- package/dist/esm/shared/constant/typeorm-aws-connector/constant.d.ts +7 -0
- package/dist/esm/shared/constant/typeorm-aws-connector/constant.js +70 -2
- package/dist/esm/shared/constant/typeorm-aws-connector/constant.js.map +1 -1
- package/dist/esm/shared/enum/database/type.enum.js.map +1 -1
- package/dist/esm/shared/enum/relation-load-strategy.enum.js.map +1 -1
- package/dist/esm/shared/interface/aws/database-credentials-secret.interface.d.ts +4 -0
- package/dist/esm/shared/interface/aws/index.d.ts +1 -0
- package/dist/esm/shared/interface/typeorm-aws-connector/config.interface.d.ts +11 -12
- package/dist/esm/shared/interface/typeorm-aws-connector/index.d.ts +3 -0
- package/dist/esm/shared/interface/typeorm-aws-connector/module-async-options.interface.d.ts +4 -4
- package/dist/esm/shared/interface/typeorm-aws-connector/ssm-lookup-defaults.interface.d.ts +2 -0
- package/dist/esm/shared/interface/typeorm-aws-connector/ssm-lookups.interface.d.ts +16 -0
- package/dist/esm/shared/interface/typeorm-aws-connector/structured-lookup.interface.d.ts +2 -0
- package/dist/esm/shared/type/database/config-rotation.type.d.ts +1 -3
- package/package.json +32 -31
- package/dist/cjs/shared/interface/aws/secrets-manager/index.d.ts +0 -1
- package/dist/cjs/shared/interface/aws/secrets-manager/item.interface.d.ts +0 -4
- package/dist/esm/shared/interface/aws/secrets-manager/index.d.ts +0 -1
- package/dist/esm/shared/interface/aws/secrets-manager/item.interface.d.ts +0 -4
package/README.md
CHANGED
|
@@ -1,337 +1,446 @@
|
|
|
1
|
+
<a id="top"></a>
|
|
2
|
+
|
|
1
3
|
<p align="center">
|
|
2
|
-
<img src="https://6jft62zmy9nx2oea.public.blob.vercel-storage.com/nestjs-typeorm-aws-connector-moPeh5TktoUIqqecoEMyixlLWpCIfL.png" width="
|
|
4
|
+
<img src="https://6jft62zmy9nx2oea.public.blob.vercel-storage.com/nestjs-typeorm-aws-connector-moPeh5TktoUIqqecoEMyixlLWpCIfL.png" width="700" alt="project-logo">
|
|
3
5
|
</p>
|
|
4
6
|
|
|
5
|
-
<h1 align="center"
|
|
6
|
-
<p align="center"><em>Seamlessly
|
|
7
|
+
<h1 align="center">🔌 NestJS-TypeORM-AWS-Connector</h1>
|
|
8
|
+
<p align="center"><em>Seamlessly bridge TypeORM with AWS Parameter Store & Secrets Manager for secure, auto-rotating database connections in NestJS</em></p>
|
|
7
9
|
|
|
8
10
|
<p align="center">
|
|
9
11
|
<a aria-label="ElsiKora logo" href="https://elsikora.com">
|
|
10
12
|
<img src="https://img.shields.io/badge/MADE%20BY%20ElsiKora-333333.svg?style=for-the-badge" alt="ElsiKora">
|
|
11
|
-
</a> <img src="https://img.shields.io/badge/
|
|
13
|
+
</a> <img src="https://img.shields.io/badge/TypeScript-3178C6.svg?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript"> <img src="https://img.shields.io/badge/NestJS-E0234E.svg?style=for-the-badge&logo=nestjs&logoColor=white" alt="NestJS"> <img src="https://img.shields.io/badge/Node.js-339933.svg?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js"> <img src="https://img.shields.io/badge/AWS-232F3E.svg?style=for-the-badge&logo=amazon-aws&logoColor=white" alt="AWS"> <img src="https://img.shields.io/badge/Rollup-EC4A3F.svg?style=for-the-badge&logo=rollup&logoColor=white" alt="Rollup"> <img src="https://img.shields.io/badge/ESLint-4B32C3.svg?style=for-the-badge&logo=eslint&logoColor=white" alt="ESLint"> <img src="https://img.shields.io/badge/Prettier-F7B93E.svg?style=for-the-badge&logo=prettier&logoColor=black" alt="Prettier"> <img src="https://img.shields.io/badge/GitHub%20Actions-2088FF.svg?style=for-the-badge&logo=github-actions&logoColor=white" alt="GitHub Actions"> <img src="https://img.shields.io/badge/npm-CB3837.svg?style=for-the-badge&logo=npm&logoColor=white" alt="npm"> <img src="https://img.shields.io/badge/PostgreSQL-4169E1.svg?style=for-the-badge&logo=postgresql&logoColor=white" alt="PostgreSQL"> <img src="https://img.shields.io/badge/MySQL-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white" alt="MySQL">
|
|
12
14
|
</p>
|
|
13
15
|
|
|
16
|
+
## 💡 Highlights
|
|
17
|
+
|
|
18
|
+
- 🔐 Zero-credential codebase — resolves all database config from AWS SSM Parameter Store & Secrets Manager at runtime
|
|
19
|
+
- ♻️ Built-in automatic credential rotation with health checks, emergency recovery, and zero-downtime connection swaps
|
|
20
|
+
- 🏗️ Split-source architecture — infra teams own host/port/secrets, app teams own pool/timeout/sync settings independently
|
|
21
|
+
- 📦 Dual ESM/CJS module output with full TypeScript declarations and broad NestJS compatibility (v8–v11)
|
|
14
22
|
|
|
15
23
|
## 📚 Table of Contents
|
|
16
24
|
- [Description](#-description)
|
|
25
|
+
- [Tech Stack](#-tech-stack)
|
|
17
26
|
- [Features](#-features)
|
|
27
|
+
- [Architecture](#-architecture)
|
|
28
|
+
- [Project Structure](#-project-structure)
|
|
29
|
+
- [Prerequisites](#-prerequisites)
|
|
18
30
|
- [Installation](#-installation)
|
|
19
31
|
- [Usage](#-usage)
|
|
20
32
|
- [Roadmap](#-roadmap)
|
|
21
33
|
- [FAQ](#-faq)
|
|
22
34
|
- [License](#-license)
|
|
23
|
-
|
|
35
|
+
- [Acknowledgments](#-acknowledgments)
|
|
24
36
|
|
|
25
37
|
## 📖 Description
|
|
26
|
-
NestJS-TypeORM-AWS-Connector
|
|
38
|
+
**NestJS-TypeORM-AWS-Connector** is a production-grade NestJS module that eliminates the pain of managing database configuration in AWS-hosted applications. Instead of hardcoding credentials or juggling environment variables, this connector resolves your entire TypeORM `DataSourceOptions` from **AWS Systems Manager Parameter Store** and **AWS Secrets Manager** — with built-in credential rotation.
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
- ✨ **Secure credential management with AWS Secrets Manager integration**
|
|
30
|
-
- ✨ **Automatic database connection rotation to mitigate credential leaks**
|
|
31
|
-
- ✨ **Configurable connection pooling for optimal database performance**
|
|
32
|
-
- ✨ **Support for multiple database types (MySQL, PostgreSQL)**
|
|
33
|
-
- ✨ **Customizable relation load strategies for performance optimization**
|
|
34
|
-
- ✨ **Failure recovery mechanism for maintaining database connectivity**
|
|
35
|
-
- ✨ **Easy integration with NestJS application configurations**
|
|
36
|
-
- ✨ **Support for both synchronous and asynchronous module registration**
|
|
37
|
-
- ✨ **Extensive logging for debugging and monitoring**
|
|
38
|
-
- ✨ **Zero configuration option with sensible defaults**
|
|
40
|
+
In modern cloud-native architectures, database credentials are rotated regularly for security compliance. Traditional approaches break connections during rotation, causing downtime. This connector solves that with an intelligent **automatic credential rotation service** that seamlessly refreshes database connections with zero downtime, including emergency recovery after consecutive failures.
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
# Using npm
|
|
43
|
-
npm install @elsikora/nestjs-typeorm-aws-connector
|
|
42
|
+
### Real-World Use Cases
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
- **ECS Fargate Microservices**: Deploy NestJS APIs on Fargate where credentials are managed by AWS Secrets Manager with automatic rotation policies — the connector picks up fresh credentials without redeployment.
|
|
45
|
+
- **Multi-Environment Deployments**: Use the same codebase across `dev`, `staging`, and `production` — each environment resolves its own database configuration from namespaced SSM parameters.
|
|
46
|
+
- **Aurora PostgreSQL / RDS MySQL**: Connect to managed AWS databases where infrastructure teams control host, port, and secret references via Parameter Store, while application teams only configure entities and behavior.
|
|
47
|
+
- **Compliance-First Organizations**: Meet SOC2 and PCI-DSS requirements by never storing credentials in code, environment files, or container images.
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
pnpm add @elsikora/nestjs-typeorm-aws-connector
|
|
49
|
+
The connector uses a **split-source model**: infrastructure-owned parameters (host, port, secret ID) live in canonical AWS namespaces, while application-owned settings (pool size, timeouts, sync behavior) live in your app's namespace. Raw value overrides let you bypass SSM entirely for local development.
|
|
50
50
|
|
|
51
|
+
## 🛠️ Tech Stack
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
| Category | Technologies |
|
|
54
|
+
|----------|-------------|
|
|
55
|
+
| **Language** | TypeScript |
|
|
56
|
+
| **Runtime** | Node.js |
|
|
57
|
+
| **Framework** | NestJS |
|
|
58
|
+
| **ORM** | TypeORM |
|
|
59
|
+
| **Cloud Services** | AWS SSM Parameter Store, AWS Secrets Manager |
|
|
60
|
+
| **Build Tool** | Rollup |
|
|
61
|
+
| **Linting** | ESLint, Prettier |
|
|
62
|
+
| **CI/CD** | GitHub Actions, Semantic Release |
|
|
63
|
+
| **Package Manager** | npm |
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
- AWS
|
|
56
|
-
-
|
|
65
|
+
## 🚀 Features
|
|
66
|
+
- ✨ ****AWS Parameter Store Integration** — Resolves database host, port, name, type, and tuning parameters from structured SSM paths with hierarchical namespace support**
|
|
67
|
+
- ✨ ****AWS Secrets Manager Integration** — Fetches username/password credentials from Secrets Manager with proper error handling for missing or malformed secrets**
|
|
68
|
+
- ✨ ****Automatic Credential Rotation** — Configurable interval-based rotation that swaps database connections with zero downtime, including subscriber preservation**
|
|
69
|
+
- ✨ ****Emergency Recovery** — After 3 consecutive rotation failures, automatically attempts full connection recovery with fresh credentials from AWS**
|
|
70
|
+
- ✨ ****Connection Health Validation** — Validates both existing and new connections with `SELECT 1` queries before and after rotation**
|
|
71
|
+
- ✨ ****Split-Source Configuration Model** — Infrastructure-owned settings (host, port, secret-id) use canonical AWS namespaces; app-owned settings use your module namespace**
|
|
72
|
+
- ✨ ****Raw Value Overrides** — Bypass SSM entirely for any field by providing direct values — perfect for local development with `host: '127.0.0.1'`**
|
|
73
|
+
- ✨ ****Hierarchical Lookup Resolution** — Field-level SSM lookup → canonical defaults → ssmLookupDefaults → ParameterStoreConfigModule defaults**
|
|
74
|
+
- ✨ ****Dual Module Format** — Ships both ESM and CJS builds with full TypeScript declarations and source maps**
|
|
75
|
+
- ✨ ****Comprehensive Error Model** — Contextual error messages include the failing field name and full structured lookup context for fast debugging**
|
|
76
|
+
- ✨ ****Broad NestJS Compatibility** — Supports NestJS v8 through v11 as peer dependencies**
|
|
77
|
+
- ✨ ****Sync & Async Registration** — Use `register()` for static config or `registerAsync()` with factory functions for dynamic configuration**
|
|
78
|
+
|
|
79
|
+
## 🏗 Architecture
|
|
80
|
+
|
|
81
|
+
### System Architecture
|
|
82
|
+
|
|
83
|
+
```mermaid
|
|
84
|
+
flowchart TD
|
|
85
|
+
appModule[AppModule] --> paramStore[ParameterStoreConfigModule]
|
|
86
|
+
appModule --> connectorModule[TypeOrmAwsConnectorModule]
|
|
87
|
+
connectorModule --> connectorService[TypeOrmAwsConnectorService]
|
|
88
|
+
connectorModule --> rotatorService[RotatorService]
|
|
89
|
+
connectorModule --> scheduleModule[ScheduleModule]
|
|
90
|
+
connectorService --> paramStoreService[ParameterStoreConfigService]
|
|
91
|
+
connectorService --> configService[NestJS ConfigService]
|
|
92
|
+
connectorService --> ssmParameterStore[AWS SSM Parameter Store]
|
|
93
|
+
connectorService --> secretsManager[AWS Secrets Manager]
|
|
94
|
+
rotatorService --> connectorService
|
|
95
|
+
rotatorService --> dataSource[TypeORM DataSource]
|
|
96
|
+
connectorModule --> typeOrmModule[TypeOrmModule.forRootAsync]
|
|
57
97
|
```
|
|
58
98
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
99
|
+
### Data Flow
|
|
100
|
+
|
|
101
|
+
```mermaid
|
|
102
|
+
sequenceDiagram
|
|
103
|
+
participant App as NestJS App
|
|
104
|
+
participant Connector as ConnectorService
|
|
105
|
+
participant SSM as AWS Parameter Store
|
|
106
|
+
participant SM as AWS Secrets Manager
|
|
107
|
+
participant TypeORM as TypeORM DataSource
|
|
108
|
+
participant Rotator as RotatorService
|
|
109
|
+
|
|
110
|
+
App->>Connector: getTypeOrmOptions()
|
|
111
|
+
Connector->>SSM: Resolve host, port, dbName, type
|
|
112
|
+
SSM-->>Connector: Parameter values
|
|
113
|
+
Connector->>SSM: Resolve secretId
|
|
114
|
+
SSM-->>Connector: Secret ID
|
|
115
|
+
Connector->>SM: GetSecretValue(secretId)
|
|
116
|
+
SM-->>Connector: username + password
|
|
117
|
+
Connector->>SSM: Resolve optional tuning params
|
|
118
|
+
SSM-->>Connector: poolSize, timeouts, etc.
|
|
119
|
+
Connector-->>App: DataSourceOptions
|
|
120
|
+
App->>TypeORM: Initialize DataSource
|
|
121
|
+
|
|
122
|
+
loop Every rotation interval
|
|
123
|
+
Rotator->>TypeORM: Validate connection health
|
|
124
|
+
Rotator->>Connector: getTypeOrmOptions()
|
|
125
|
+
Connector->>SM: Fetch fresh credentials
|
|
126
|
+
SM-->>Connector: New username + password
|
|
127
|
+
Rotator->>TypeORM: Destroy old connection
|
|
128
|
+
Rotator->>TypeORM: Initialize new DataSource
|
|
129
|
+
Rotator->>TypeORM: Verify with SELECT 1
|
|
130
|
+
Rotator->>TypeORM: Swap driver + restore subscribers
|
|
131
|
+
end
|
|
132
|
+
```
|
|
62
133
|
|
|
63
|
-
|
|
64
|
-
2. Familiarize yourself with its documentation and setup
|
|
65
|
-
3. Ensure it's properly configured in your NestJS application
|
|
134
|
+
## 📁 Project Structure
|
|
66
135
|
|
|
67
|
-
|
|
136
|
+
<details>
|
|
137
|
+
<summary>Click to expand</summary>
|
|
68
138
|
|
|
69
|
-
```typescript
|
|
70
|
-
@Module({
|
|
71
|
-
imports: [
|
|
72
|
-
// First, configure Parameter Store
|
|
73
|
-
ParameterStoreConfigModule.registerAsync({
|
|
74
|
-
imports: [ConfigModule],
|
|
75
|
-
inject: [ConfigService],
|
|
76
|
-
useFactory: (config: ConfigService) => ({
|
|
77
|
-
application: config.get<string>("APPLICATION"),
|
|
78
|
-
config: {
|
|
79
|
-
region: config.get<string>("AWS_REGION"),
|
|
80
|
-
},
|
|
81
|
-
decryptParameters: true,
|
|
82
|
-
environment: config.get<string>("ENVIRONMENT"),
|
|
83
|
-
recursiveLoading: true,
|
|
84
|
-
}),
|
|
85
|
-
}),
|
|
86
|
-
// Then, configure Database module
|
|
87
|
-
TypeOrmAwsConnectorModule.registerAsync({
|
|
88
|
-
imports: [ConfigModule],
|
|
89
|
-
inject: [ConfigService],
|
|
90
|
-
useFactory: async () => ({
|
|
91
|
-
connectionTimeoutMs: 30_000,
|
|
92
|
-
databaseName: "app",
|
|
93
|
-
entities: [Bank],
|
|
94
|
-
// ... other options
|
|
95
|
-
}),
|
|
96
|
-
}),
|
|
97
|
-
],
|
|
98
|
-
})
|
|
99
|
-
export class AppModule {}
|
|
100
139
|
```
|
|
140
|
+
NestJS-TypeORM-AWS-Connector/
|
|
141
|
+
├── .github/
|
|
142
|
+
│ ├── workflows/
|
|
143
|
+
│ │ ├── mirror-to-codecommit.yml
|
|
144
|
+
│ │ ├── qodana-quality-scan.yml
|
|
145
|
+
│ │ ├── release.yml
|
|
146
|
+
│ │ └── snyk-security-scan.yml
|
|
147
|
+
│ └── dependabot.yml
|
|
148
|
+
├── src/
|
|
149
|
+
│ ├── modules/
|
|
150
|
+
│ │ └── typeorm-aws-connector/
|
|
151
|
+
│ ├── shared/
|
|
152
|
+
│ │ ├── constant/
|
|
153
|
+
│ │ ├── enum/
|
|
154
|
+
│ │ ├── interface/
|
|
155
|
+
│ │ ├── provider/
|
|
156
|
+
│ │ └── type/
|
|
157
|
+
│ └── index.ts
|
|
158
|
+
├── CHANGELOG.md
|
|
159
|
+
├── commitlint.config.js
|
|
160
|
+
├── eslint.config.js
|
|
161
|
+
├── LICENSE
|
|
162
|
+
├── lint-staged.config.js
|
|
163
|
+
├── nest-cli.json
|
|
164
|
+
├── package-lock.json
|
|
165
|
+
├── package.json
|
|
166
|
+
├── prettier.config.js
|
|
167
|
+
├── README.md
|
|
168
|
+
├── release.config.js
|
|
169
|
+
├── rollup.config.js
|
|
170
|
+
├── tsconfig.build.json
|
|
171
|
+
└── tsconfig.json
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
</details>
|
|
175
|
+
|
|
176
|
+
## 📋 Prerequisites
|
|
177
|
+
|
|
178
|
+
- Node.js >= 18.0.0
|
|
179
|
+
- npm >= 9.0.0
|
|
180
|
+
- @nestjs/common ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
|
|
181
|
+
- @aws-sdk/client-ssm ^3.535.0
|
|
182
|
+
- typeorm ^0.3.20
|
|
183
|
+
- @elsikora/nestjs-aws-parameter-store-config ^2.0.1 (installed automatically)
|
|
184
|
+
- AWS credentials configured (IAM role, environment variables, or AWS CLI profile)
|
|
185
|
+
|
|
186
|
+
## 🛠 Installation
|
|
187
|
+
```bash
|
|
188
|
+
# Install the connector and its required peer dependencies
|
|
189
|
+
npm install @elsikora/nestjs-typeorm-aws-connector @aws-sdk/client-ssm @nestjs/common typeorm
|
|
101
190
|
|
|
102
|
-
|
|
191
|
+
# The following are installed automatically as dependencies:
|
|
192
|
+
# @aws-sdk/client-secrets-manager
|
|
193
|
+
# @elsikora/nestjs-aws-parameter-store-config
|
|
194
|
+
# @nestjs/config
|
|
195
|
+
# @nestjs/schedule
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
### Verify Installation
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
npm ls @elsikora/nestjs-typeorm-aws-connector
|
|
202
|
+
```
|
|
103
203
|
|
|
104
204
|
## 💡 Usage
|
|
105
|
-
|
|
205
|
+
### Prerequisites: Register Parameter Store Config Module
|
|
106
206
|
|
|
107
|
-
|
|
207
|
+
Before using the connector, register `ParameterStoreConfigModule` from `@elsikora/nestjs-aws-parameter-store-config`:
|
|
108
208
|
|
|
109
209
|
```typescript
|
|
110
|
-
import { Module } from
|
|
111
|
-
import {
|
|
112
|
-
import {
|
|
113
|
-
import { UserEntity } from './entities/user.entity';
|
|
210
|
+
import { Module } from "@nestjs/common";
|
|
211
|
+
import { ConfigModule, ConfigService } from "@nestjs/config";
|
|
212
|
+
import { ENamespace, ParameterStoreConfigModule } from "@elsikora/nestjs-aws-parameter-store-config";
|
|
114
213
|
|
|
115
214
|
@Module({
|
|
116
215
|
imports: [
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
216
|
+
ConfigModule.forRoot(),
|
|
217
|
+
ParameterStoreConfigModule.registerAsync({
|
|
218
|
+
imports: [ConfigModule],
|
|
219
|
+
inject: [ConfigService],
|
|
220
|
+
useFactory: (configService: ConfigService) => ({
|
|
221
|
+
application: configService.getOrThrow<string>("APPLICATION"),
|
|
222
|
+
config: { region: configService.getOrThrow<string>("AWS_REGION") },
|
|
223
|
+
environment: configService.getOrThrow<string>("ENVIRONMENT"),
|
|
224
|
+
instanceName: "my-api",
|
|
225
|
+
namespace: ENamespace.AWS_ECS_FARGATE,
|
|
226
|
+
shouldDecryptParameters: true,
|
|
227
|
+
}),
|
|
127
228
|
}),
|
|
128
229
|
],
|
|
129
230
|
})
|
|
130
231
|
export class AppModule {}
|
|
131
232
|
```
|
|
132
233
|
|
|
133
|
-
|
|
234
|
+
---
|
|
134
235
|
|
|
135
|
-
|
|
236
|
+
### Basic Usage: Static Registration
|
|
136
237
|
|
|
137
238
|
```typescript
|
|
138
|
-
import { Module } from
|
|
139
|
-
import {
|
|
140
|
-
import {
|
|
141
|
-
|
|
239
|
+
import { Module } from "@nestjs/common";
|
|
240
|
+
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
241
|
+
import {
|
|
242
|
+
TypeOrmAwsConnectorModule,
|
|
243
|
+
TypeOrmAwsConnectorService,
|
|
244
|
+
} from "@elsikora/nestjs-typeorm-aws-connector";
|
|
245
|
+
import { UserEntity } from "./entities/user.entity";
|
|
142
246
|
|
|
143
247
|
@Module({
|
|
144
248
|
imports: [
|
|
145
|
-
|
|
146
|
-
TypeOrmAwsConnectorModule.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
isEnabled: configService.get('DB_ROTATION_ENABLED') === 'true',
|
|
156
|
-
},
|
|
157
|
-
}),
|
|
249
|
+
// Register the connector with your entities
|
|
250
|
+
TypeOrmAwsConnectorModule.register({
|
|
251
|
+
entities: [UserEntity],
|
|
252
|
+
}),
|
|
253
|
+
// Wire it into TypeOrmModule
|
|
254
|
+
TypeOrmModule.forRootAsync({
|
|
255
|
+
imports: [TypeOrmAwsConnectorModule],
|
|
256
|
+
inject: [TypeOrmAwsConnectorService],
|
|
257
|
+
useFactory: async (connector: TypeOrmAwsConnectorService) =>
|
|
258
|
+
connector.getTypeOrmOptions(),
|
|
158
259
|
}),
|
|
159
260
|
],
|
|
160
261
|
})
|
|
161
|
-
export class
|
|
262
|
+
export class DatabaseModule {}
|
|
162
263
|
```
|
|
163
264
|
|
|
164
|
-
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
### Custom SSM Lookups
|
|
165
268
|
|
|
166
|
-
|
|
269
|
+
Override where specific fields are resolved from in Parameter Store:
|
|
167
270
|
|
|
168
271
|
```typescript
|
|
169
|
-
import {
|
|
170
|
-
import {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
272
|
+
import { ENamespace } from "@elsikora/nestjs-aws-parameter-store-config";
|
|
273
|
+
import { TypeOrmAwsConnectorModule } from "@elsikora/nestjs-typeorm-aws-connector";
|
|
274
|
+
|
|
275
|
+
TypeOrmAwsConnectorModule.register({
|
|
276
|
+
entities: [UserEntity],
|
|
277
|
+
ssmLookupDefaults: {
|
|
278
|
+
instanceName: "my-api",
|
|
279
|
+
namespace: ENamespace.AWS_ECS_FARGATE,
|
|
280
|
+
},
|
|
281
|
+
ssmLookups: {
|
|
282
|
+
secretId: {
|
|
283
|
+
instanceName: "database",
|
|
284
|
+
namespace: ENamespace.AWS_SECRETS_MANAGER,
|
|
285
|
+
path: ["secret-id"],
|
|
286
|
+
},
|
|
287
|
+
host: {
|
|
288
|
+
instanceName: "aurora-postgres",
|
|
289
|
+
namespace: ENamespace.AWS_RDS,
|
|
290
|
+
path: ["host"],
|
|
291
|
+
},
|
|
292
|
+
port: {
|
|
293
|
+
instanceName: "aurora-postgres",
|
|
294
|
+
namespace: ENamespace.AWS_RDS,
|
|
295
|
+
path: ["port"],
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
});
|
|
186
299
|
```
|
|
187
300
|
|
|
188
|
-
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
### Local Development: Raw Value Overrides
|
|
189
304
|
|
|
190
|
-
|
|
305
|
+
Bypass AWS entirely for local development:
|
|
191
306
|
|
|
192
307
|
```typescript
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// /myapp/database-credentials-secret-id -> 'db-credentials-secret-id'
|
|
196
|
-
// /myapp/host -> 'your-database-hostname'
|
|
197
|
-
|
|
198
|
-
// In AWS Secrets Manager (with ID 'db-credentials-secret-id'):
|
|
199
|
-
// {
|
|
200
|
-
// "username": "dbuser",
|
|
201
|
-
// "password": "dbpassword"
|
|
202
|
-
// }
|
|
203
|
-
|
|
204
|
-
// Your NestJS module configuration
|
|
308
|
+
import { EDatabaseType } from "@elsikora/nestjs-typeorm-aws-connector";
|
|
309
|
+
|
|
205
310
|
TypeOrmAwsConnectorModule.register({
|
|
206
|
-
|
|
207
|
-
|
|
311
|
+
entities: [UserEntity],
|
|
312
|
+
host: "127.0.0.1",
|
|
208
313
|
port: 5432,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
host: {
|
|
215
|
-
path: ['myapp', 'host'],
|
|
216
|
-
service: EService.AWS_RDS
|
|
217
|
-
}
|
|
218
|
-
})
|
|
314
|
+
username: "local-user",
|
|
315
|
+
password: "local-password",
|
|
316
|
+
databaseName: "mydb",
|
|
317
|
+
type: EDatabaseType.POSTGRES,
|
|
318
|
+
});
|
|
219
319
|
```
|
|
220
320
|
|
|
221
|
-
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
### Enabling Credential Rotation
|
|
222
324
|
|
|
223
|
-
Enable automatic credential rotation for
|
|
325
|
+
Enable automatic credential rotation for long-running services:
|
|
224
326
|
|
|
225
327
|
```typescript
|
|
226
328
|
TypeOrmAwsConnectorModule.register({
|
|
227
|
-
// Basic config...
|
|
228
|
-
type: EDatabaseType.POSTGRES,
|
|
229
|
-
databaseName: 'myapp_db',
|
|
230
|
-
port: 5432,
|
|
231
329
|
entities: [UserEntity],
|
|
232
|
-
|
|
233
|
-
// Rotation config
|
|
234
330
|
rotation: {
|
|
235
331
|
isEnabled: true,
|
|
236
|
-
intervalMs:
|
|
332
|
+
intervalMs: 3_600_000, // Rotate every hour
|
|
237
333
|
},
|
|
238
|
-
|
|
239
|
-
// Advanced connection pool settings
|
|
240
|
-
poolSize: 20,
|
|
241
|
-
connectionTimeoutMs: 60000,
|
|
242
|
-
idleTimeoutMs: 30000
|
|
243
|
-
})
|
|
334
|
+
});
|
|
244
335
|
```
|
|
245
336
|
|
|
246
|
-
|
|
337
|
+
The rotation service will:
|
|
338
|
+
1. Validate current connection health
|
|
339
|
+
2. Fetch fresh credentials from AWS Secrets Manager
|
|
340
|
+
3. Create a new DataSource with updated credentials
|
|
341
|
+
4. Verify the new connection with a `SELECT 1` query
|
|
342
|
+
5. Swap the driver seamlessly, preserving all subscribers
|
|
343
|
+
6. Attempt emergency recovery after 3 consecutive failures
|
|
247
344
|
|
|
248
|
-
|
|
345
|
+
---
|
|
249
346
|
|
|
250
|
-
|
|
251
|
-
import { ERelationLoadStrategy } from '@elsikora/nestjs-typeorm-aws-connector';
|
|
347
|
+
### Async Registration with Factory
|
|
252
348
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// Database behavior
|
|
266
|
-
shouldSynchronize: false,
|
|
267
|
-
isVerbose: true,
|
|
268
|
-
relationLoadStrategy: ERelationLoadStrategy.QUERY,
|
|
269
|
-
|
|
270
|
-
// AWS configuration
|
|
271
|
-
secretID: {
|
|
272
|
-
path: ['my-app', 'db-secret-id'],
|
|
273
|
-
service: EService.AWS_SECRETS_MANAGER
|
|
274
|
-
},
|
|
275
|
-
host: {
|
|
276
|
-
path: ['my-app', 'db-host'],
|
|
277
|
-
service: EService.AWS_RDS
|
|
278
|
-
}
|
|
279
|
-
})
|
|
349
|
+
```typescript
|
|
350
|
+
TypeOrmAwsConnectorModule.registerAsync({
|
|
351
|
+
imports: [ConfigModule],
|
|
352
|
+
inject: [ConfigService],
|
|
353
|
+
useFactory: (configService: ConfigService) => ({
|
|
354
|
+
entities: [UserEntity],
|
|
355
|
+
rotation: {
|
|
356
|
+
isEnabled: configService.get<boolean>("DB_ROTATION_ENABLED", false),
|
|
357
|
+
intervalMs: configService.get<number>("DB_ROTATION_INTERVAL", 3_600_000),
|
|
358
|
+
},
|
|
359
|
+
}),
|
|
360
|
+
});
|
|
280
361
|
```
|
|
281
362
|
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
### Value Resolution Priority
|
|
366
|
+
|
|
367
|
+
For each configuration field, values are resolved in this order:
|
|
368
|
+
|
|
369
|
+
1. **Raw value override** (provided directly in config)
|
|
370
|
+
2. **SSM Parameter Store lookup** (via structured lookup)
|
|
371
|
+
3. **Built-in default** (only for optional tuning fields like `poolSize`, `connectionTimeoutMs`)
|
|
372
|
+
|
|
373
|
+
Required fields (`host`, `port`, `databaseName`, `type`, `secretId`) throw explicit errors if not resolvable.
|
|
374
|
+
|
|
282
375
|
## 🛣 Roadmap
|
|
376
|
+
|
|
377
|
+
<details>
|
|
378
|
+
<summary>Click to expand</summary>
|
|
379
|
+
|
|
283
380
|
| Task / Feature | Status |
|
|
284
|
-
|
|
285
|
-
|
|
|
286
|
-
| AWS Secrets Manager
|
|
287
|
-
|
|
|
288
|
-
|
|
|
289
|
-
|
|
|
290
|
-
|
|
|
291
|
-
|
|
|
292
|
-
|
|
|
293
|
-
|
|
|
294
|
-
| Connection
|
|
295
|
-
|
|
|
296
|
-
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
| Connection health metrics | 🚧 In Progress |
|
|
300
|
-
| Multi-database connection management | 🚧 In Progress |
|
|
301
|
-
| Docker integration examples | 🚧 In Progress |
|
|
302
|
-
| AWS Lambda optimized configuration | 🚧 In Progress |
|
|
381
|
+
|---|---|
|
|
382
|
+
| Dual ESM/CJS module output | ✅ Done |
|
|
383
|
+
| AWS Secrets Manager credential resolution | ✅ Done |
|
|
384
|
+
| Automatic credential rotation with zero downtime | ✅ Done |
|
|
385
|
+
| Emergency recovery after consecutive rotation failures | ✅ Done |
|
|
386
|
+
| Split-source configuration model (infra vs app namespaces) | ✅ Done |
|
|
387
|
+
| Raw value overrides for local development | ✅ Done |
|
|
388
|
+
| Async module registration with factory pattern | ✅ Done |
|
|
389
|
+
| Semantic release with prerelease channel | ✅ Done |
|
|
390
|
+
| Support for additional database types (e.g., MariaDB, CockroachDB) | 🚧 In Progress |
|
|
391
|
+
| Connection pool monitoring and metrics export | 🚧 In Progress |
|
|
392
|
+
| Read replica support with separate SSM lookups | 🚧 In Progress |
|
|
393
|
+
| Integration test suite with LocalStack | 🚧 In Progress |
|
|
394
|
+
|
|
395
|
+
</details>
|
|
303
396
|
|
|
304
397
|
## ❓ FAQ
|
|
305
|
-
### Frequently Asked Questions
|
|
306
398
|
|
|
307
|
-
|
|
308
|
-
|
|
399
|
+
<details>
|
|
400
|
+
<summary>Click to expand</summary>
|
|
309
401
|
|
|
310
|
-
|
|
311
|
-
A: When rotation is enabled, the connector periodically fetches fresh credentials from AWS Secrets Manager without disrupting existing connections. It creates a new connection pool with updated credentials, verifies it works correctly, and then seamlessly replaces the old connection.
|
|
402
|
+
### ❓ Frequently Asked Questions
|
|
312
403
|
|
|
313
|
-
|
|
314
|
-
A:
|
|
404
|
+
**Q: Do I need AWS credentials to use this locally?**
|
|
405
|
+
A: No! Use raw value overrides to bypass AWS entirely during local development. Set `host`, `port`, `username`, `password`, `databaseName`, and `type` directly in the config.
|
|
315
406
|
|
|
316
|
-
|
|
317
|
-
A:
|
|
407
|
+
**Q: Which databases are supported?**
|
|
408
|
+
A: Currently PostgreSQL (`EDatabaseType.POSTGRES`) and MySQL (`EDatabaseType.MYSQL`). The `type` field is resolved from SSM or set directly.
|
|
318
409
|
|
|
319
|
-
|
|
320
|
-
A:
|
|
410
|
+
**Q: What happens if AWS Secrets Manager is unreachable during rotation?**
|
|
411
|
+
A: The rotation service catches the error, increments a failure counter, and logs it. After 3 consecutive failures, it attempts an emergency recovery with a completely fresh DataSource. The existing connection continues to function until rotation succeeds.
|
|
321
412
|
|
|
322
|
-
|
|
323
|
-
A:
|
|
413
|
+
**Q: Can I use this without `@elsikora/nestjs-aws-parameter-store-config`?**
|
|
414
|
+
A: No. The connector depends on `ParameterStoreConfigService.get()` for SSM lookups. You must register `ParameterStoreConfigModule` first. However, if you provide raw values for all fields, SSM lookups won't actually be invoked.
|
|
324
415
|
|
|
325
|
-
|
|
326
|
-
A:
|
|
416
|
+
**Q: How does the split-source model work?**
|
|
417
|
+
A: Infrastructure-owned fields like `host`, `port`, and `secretId` default to canonical AWS namespaces (e.g., `aws-rds/aurora-postgres`). Application-owned fields like `poolSize` and `connectionTimeoutMs` use your app's namespace (e.g., `aws-ecs-fargate/my-api`). You can override any lookup.
|
|
327
418
|
|
|
328
|
-
|
|
329
|
-
|
|
419
|
+
**Q: Is this compatible with NestJS v8?**
|
|
420
|
+
A: Yes. The peer dependency accepts `@nestjs/common ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0`.
|
|
330
421
|
|
|
331
|
-
|
|
422
|
+
**Q: How do I disable credential rotation?**
|
|
423
|
+
A: Don't set the `rotation` config, or explicitly set `rotation.isEnabled: false`. The `RotatorService` will skip interval registration entirely.
|
|
332
424
|
|
|
333
|
-
|
|
425
|
+
**Q: What's the default rotation interval?**
|
|
426
|
+
A: 1 hour (3,600,000 ms). Override it via `rotation.intervalMs` or the SSM path `typeorm/rotation/interval-ms`.
|
|
334
427
|
|
|
335
|
-
|
|
428
|
+
</details>
|
|
336
429
|
|
|
337
|
-
|
|
430
|
+
## 🔒 License
|
|
431
|
+
This project is licensed under **MIT**.
|
|
432
|
+
|
|
433
|
+
## 🙏 Acknowledgments
|
|
434
|
+
- **[NestJS](https://nestjs.com/)** — The progressive Node.js framework that makes building server-side applications a joy
|
|
435
|
+
- **[TypeORM](https://typeorm.io/)** — The ORM that powers the database layer with decorator-based entity definitions
|
|
436
|
+
- **[AWS SDK for JavaScript v3](https://github.com/aws/aws-sdk-js-v3)** — Modular AWS SDK used for SSM and Secrets Manager client operations
|
|
437
|
+
- **[@elsikora/nestjs-aws-parameter-store-config](https://github.com/ElsiKora/nestjs-aws-parameter-store-config)** — The Parameter Store config module that provides the structured lookup foundation
|
|
438
|
+
- **[Semantic Release](https://semantic-release.gitbook.io/)** — Automated versioning and changelog generation
|
|
439
|
+
- **[Rollup](https://rollupjs.org/)** — Module bundler powering the dual ESM/CJS build output
|
|
440
|
+
- Built with ❤️ by [ElsiKora](https://github.com/ElsiKora)
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
<p align="center">
|
|
445
|
+
<a href="#top">Back to Top</a>
|
|
446
|
+
</p>
|