@onivoro/server-aws-kinesis 24.39.0 → 25.0.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 +60 -218
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,277 +1,119 @@
|
|
|
1
1
|
# @onivoro/server-aws-kinesis
|
|
2
2
|
|
|
3
|
-
AWS Kinesis Data Streams integration for NestJS applications.
|
|
3
|
+
AWS Kinesis Data Streams integration for NestJS applications: publish JSON records to a stream.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @onivoro/server-aws-kinesis
|
|
8
|
+
npm install @onivoro/server-aws-kinesis @aws-sdk/client-kinesis @nestjs/common
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
This library provides a simple AWS Kinesis Data Streams integration for NestJS applications, allowing you to publish data to Kinesis streams.
|
|
11
|
+
`@aws-sdk/client-kinesis` and `@nestjs/common` are peer dependencies.
|
|
14
12
|
|
|
15
13
|
## Module Setup
|
|
16
14
|
|
|
15
|
+
`ServerAwsKinesisModule.configure(config)` takes the configuration object directly; the module does not read environment variables itself.
|
|
16
|
+
|
|
17
17
|
```typescript
|
|
18
18
|
import { Module } from '@nestjs/common';
|
|
19
19
|
import { ServerAwsKinesisModule } from '@onivoro/server-aws-kinesis';
|
|
20
20
|
|
|
21
21
|
@Module({
|
|
22
22
|
imports: [
|
|
23
|
-
ServerAwsKinesisModule.configure(
|
|
24
|
-
|
|
23
|
+
ServerAwsKinesisModule.configure({
|
|
24
|
+
AWS_REGION: process.env.AWS_REGION!,
|
|
25
|
+
AWS_KINESIS_NAME: process.env.AWS_KINESIS_NAME!,
|
|
26
|
+
AWS_PROFILE: process.env.AWS_PROFILE, // optional
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
25
29
|
})
|
|
26
30
|
export class AppModule {}
|
|
27
31
|
```
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
The module is not global. It provides and exports `KinesisService`, `ServerAwsKinesisConfig`, a `KinesisClient` instance, and the `AwsCredentials` provider from `@onivoro/server-aws-credential-providers`.
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
## Configuration
|
|
32
36
|
|
|
33
37
|
```typescript
|
|
34
38
|
export class ServerAwsKinesisConfig {
|
|
39
|
+
AWS_KINESIS_NAME: string; // stream that publish writes to
|
|
40
|
+
AWS_PROFILE?: string; // optional named profile from ~/.aws
|
|
35
41
|
AWS_REGION: string;
|
|
36
|
-
AWS_PROFILE?: string; // Optional AWS profile
|
|
37
42
|
}
|
|
38
43
|
```
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
### KinesisService
|
|
43
|
-
|
|
44
|
-
The main service for publishing data to Kinesis streams:
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
import { Injectable } from '@nestjs/common';
|
|
48
|
-
import { KinesisService } from '@onivoro/server-aws-kinesis';
|
|
49
|
-
|
|
50
|
-
@Injectable()
|
|
51
|
-
export class EventPublisherService {
|
|
52
|
-
constructor(private readonly kinesisService: KinesisService) {}
|
|
45
|
+
### AWS Credentials
|
|
53
46
|
|
|
54
|
-
|
|
55
|
-
const result = await this.kinesisService.publish({
|
|
56
|
-
streamName,
|
|
57
|
-
data: eventData,
|
|
58
|
-
partitionKey: eventData.id || 'default'
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
47
|
+
Credentials are resolved by [`@onivoro/server-aws-credential-providers`](../aws-credential-providers/):
|
|
63
48
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const data = {
|
|
67
|
-
userId,
|
|
68
|
-
activity,
|
|
69
|
-
timestamp: new Date().toISOString()
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
return await this.kinesisService.publish({
|
|
73
|
-
streamName,
|
|
74
|
-
data,
|
|
75
|
-
partitionKey: userId // Use userId as partition key for ordering
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
```
|
|
49
|
+
- If `AWS_PROFILE` is set, credentials are loaded from that profile in the shared credentials file. If that fails, `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` (or their lowercase forms) from the environment are used.
|
|
50
|
+
- If `AWS_PROFILE` is not set, the client uses the AWS SDK's default credential provider chain.
|
|
80
51
|
|
|
81
|
-
##
|
|
52
|
+
## KinesisService
|
|
82
53
|
|
|
83
|
-
### publish(
|
|
54
|
+
### `publish<TData>(event: TData, PartitionKey: string): Promise<void>`
|
|
84
55
|
|
|
85
|
-
|
|
56
|
+
Sends a `PutRecordCommand` to the `AWS_KINESIS_NAME` stream with `Data` set to `JSON.stringify(event)` and the given partition key. The stream name always comes from config.
|
|
86
57
|
|
|
87
|
-
|
|
88
|
-
- **data** (any, required): The data to publish (will be JSON stringified)
|
|
89
|
-
- **partitionKey** (string, required): Used to determine which shard to send the record to
|
|
90
|
-
|
|
91
|
-
## Direct Client Access
|
|
92
|
-
|
|
93
|
-
The service exposes the underlying Kinesis client for advanced operations:
|
|
58
|
+
Errors are caught and logged with `console.error`; `publish` never rejects and does not return the `PutRecord` response (shard ID / sequence number).
|
|
94
59
|
|
|
95
60
|
```typescript
|
|
96
|
-
import {
|
|
97
|
-
|
|
98
|
-
ListStreamsCommand,
|
|
99
|
-
GetRecordsCommand,
|
|
100
|
-
GetShardIteratorCommand,
|
|
101
|
-
CreateStreamCommand
|
|
102
|
-
} from '@aws-sdk/client-kinesis';
|
|
103
|
-
|
|
104
|
-
@Injectable()
|
|
105
|
-
export class AdvancedKinesisService {
|
|
106
|
-
constructor(private readonly kinesisService: KinesisService) {}
|
|
107
|
-
|
|
108
|
-
// List all Kinesis streams
|
|
109
|
-
async listStreams() {
|
|
110
|
-
const command = new ListStreamsCommand({});
|
|
111
|
-
return await this.kinesisService.kinesisClient.send(command);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Describe stream details
|
|
115
|
-
async describeStream(streamName: string) {
|
|
116
|
-
const command = new DescribeStreamCommand({
|
|
117
|
-
StreamName: streamName
|
|
118
|
-
});
|
|
119
|
-
return await this.kinesisService.kinesisClient.send(command);
|
|
120
|
-
}
|
|
61
|
+
import { Injectable } from '@nestjs/common';
|
|
62
|
+
import { KinesisService } from '@onivoro/server-aws-kinesis';
|
|
121
63
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
ShardCount: shardCount
|
|
127
|
-
});
|
|
128
|
-
return await this.kinesisService.kinesisClient.send(command);
|
|
129
|
-
}
|
|
64
|
+
interface OrderEvent {
|
|
65
|
+
orderId: string;
|
|
66
|
+
type: 'ORDER_CREATED' | 'ORDER_SHIPPED';
|
|
67
|
+
timestamp: string;
|
|
130
68
|
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Complete Example
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
import { Module, Injectable } from '@nestjs/common';
|
|
137
|
-
import { ServerAwsKinesisModule, KinesisService } from '@onivoro/server-aws-kinesis';
|
|
138
|
-
|
|
139
|
-
@Module({
|
|
140
|
-
imports: [ServerAwsKinesisModule.configure()],
|
|
141
|
-
providers: [OrderEventService],
|
|
142
|
-
exports: [OrderEventService]
|
|
143
|
-
})
|
|
144
|
-
export class OrderModule {}
|
|
145
69
|
|
|
146
70
|
@Injectable()
|
|
147
|
-
export class
|
|
71
|
+
export class OrderEventPublisher {
|
|
148
72
|
constructor(private readonly kinesisService: KinesisService) {}
|
|
149
73
|
|
|
150
|
-
async
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const event = {
|
|
154
|
-
orderId,
|
|
155
|
-
eventType,
|
|
156
|
-
eventData,
|
|
157
|
-
timestamp: new Date().toISOString(),
|
|
158
|
-
version: '1.0'
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
const result = await this.kinesisService.publish({
|
|
163
|
-
streamName,
|
|
164
|
-
data: event,
|
|
165
|
-
partitionKey: orderId
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
console.log(`Published ${eventType} event for order ${orderId}:`, {
|
|
169
|
-
shardId: result.ShardId,
|
|
170
|
-
sequenceNumber: result.SequenceNumber
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
return result;
|
|
174
|
-
} catch (error) {
|
|
175
|
-
console.error(`Failed to publish event for order ${orderId}:`, error);
|
|
176
|
-
throw error;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Publish different order events
|
|
181
|
-
async orderCreated(order: any) {
|
|
182
|
-
return this.publishOrderEvent(order.id, 'ORDER_CREATED', order);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async orderUpdated(orderId: string, updates: any) {
|
|
186
|
-
return this.publishOrderEvent(orderId, 'ORDER_UPDATED', updates);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async orderShipped(orderId: string, trackingInfo: any) {
|
|
190
|
-
return this.publishOrderEvent(orderId, 'ORDER_SHIPPED', trackingInfo);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async orderDelivered(orderId: string, deliveryInfo: any) {
|
|
194
|
-
return this.publishOrderEvent(orderId, 'ORDER_DELIVERED', deliveryInfo);
|
|
74
|
+
async orderCreated(orderId: string) {
|
|
75
|
+
// Using orderId as the partition key keeps an order's events on one shard, in order.
|
|
76
|
+
await this.kinesisService.publish<OrderEvent>({ orderId, type: 'ORDER_CREATED', timestamp: new Date().toISOString() }, orderId);
|
|
195
77
|
}
|
|
196
78
|
}
|
|
197
79
|
```
|
|
198
80
|
|
|
199
|
-
##
|
|
81
|
+
## Direct Client Access
|
|
200
82
|
|
|
201
|
-
|
|
83
|
+
The service keeps its client private, but the module exports the `KinesisClient` provider. Inject it for anything `publish` doesn't cover, such as batching or when you need errors to propagate:
|
|
202
84
|
|
|
203
85
|
```typescript
|
|
204
|
-
@
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
async publishBatch(streamName: string, events: any[]) {
|
|
209
|
-
// Use the exposed client for batch operations
|
|
210
|
-
const records = events.map(event => ({
|
|
211
|
-
Data: Buffer.from(JSON.stringify(event.data)),
|
|
212
|
-
PartitionKey: event.partitionKey
|
|
213
|
-
}));
|
|
214
|
-
|
|
215
|
-
const command = new PutRecordsCommand({
|
|
216
|
-
StreamName: streamName,
|
|
217
|
-
Records: records
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
return await this.kinesisService.kinesisClient.send(command);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
## Environment Variables
|
|
226
|
-
|
|
227
|
-
```bash
|
|
228
|
-
# Required: AWS region
|
|
229
|
-
AWS_REGION=us-east-1
|
|
230
|
-
|
|
231
|
-
# Optional: AWS profile
|
|
232
|
-
AWS_PROFILE=my-profile
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## AWS Credentials
|
|
236
|
-
|
|
237
|
-
The module uses the standard AWS SDK credential chain:
|
|
238
|
-
1. Environment variables
|
|
239
|
-
2. Shared credentials file
|
|
240
|
-
3. IAM roles (for EC2/ECS/Lambda)
|
|
241
|
-
|
|
242
|
-
## Error Handling
|
|
86
|
+
import { Injectable } from '@nestjs/common';
|
|
87
|
+
import { KinesisClient, PutRecordsCommand } from '@aws-sdk/client-kinesis';
|
|
88
|
+
import { ServerAwsKinesisConfig } from '@onivoro/server-aws-kinesis';
|
|
243
89
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
90
|
+
@Injectable()
|
|
91
|
+
export class BatchEventPublisher {
|
|
92
|
+
constructor(
|
|
93
|
+
private readonly kinesis: KinesisClient,
|
|
94
|
+
private readonly config: ServerAwsKinesisConfig,
|
|
95
|
+
) {}
|
|
96
|
+
|
|
97
|
+
publishBatch(events: { partitionKey: string; data: unknown }[]) {
|
|
98
|
+
return this.kinesis.send(
|
|
99
|
+
new PutRecordsCommand({
|
|
100
|
+
StreamName: this.config.AWS_KINESIS_NAME,
|
|
101
|
+
Records: events.map((e) => ({
|
|
102
|
+
Data: Buffer.from(JSON.stringify(e.data)),
|
|
103
|
+
PartitionKey: e.partitionKey,
|
|
104
|
+
})),
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
256
107
|
}
|
|
257
108
|
}
|
|
258
109
|
```
|
|
259
110
|
|
|
260
|
-
##
|
|
261
|
-
|
|
262
|
-
- This library only provides a single `publish` method
|
|
263
|
-
- No built-in support for batch publishing or consumer operations
|
|
264
|
-
- For advanced Kinesis operations, use the exposed `kinesisClient` directly
|
|
265
|
-
- No automatic retry logic for throughput exceptions
|
|
266
|
-
|
|
267
|
-
## Best Practices
|
|
111
|
+
## Exports
|
|
268
112
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
4. **Monitoring**: Use CloudWatch metrics to monitor stream performance
|
|
273
|
-
5. **Scaling**: Monitor shard metrics and scale as needed
|
|
113
|
+
- `ServerAwsKinesisModule` - dynamic module with `configure(config)`
|
|
114
|
+
- `ServerAwsKinesisConfig` - configuration class (also injectable)
|
|
115
|
+
- `KinesisService` - `publish(event, partitionKey)`
|
|
274
116
|
|
|
275
117
|
## License
|
|
276
118
|
|
|
277
|
-
MIT
|
|
119
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onivoro/server-aws-kinesis",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "25.0.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"url": "https://github.com/onivoro/monorepo.git"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@onivoro/server-aws-credential-providers": "
|
|
14
|
-
"@onivoro/server-common": "
|
|
13
|
+
"@onivoro/server-aws-credential-providers": "25.0.2",
|
|
14
|
+
"@onivoro/server-common": "25.0.2",
|
|
15
15
|
"tslib": "^2.3.0"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|