nestjs-temporal-core 2.0.5 → 2.0.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 +434 -325
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/temporal-client.module.js +6 -6
- package/dist/client/temporal-client.module.js.map +1 -1
- package/dist/client/temporal-client.service.d.ts +0 -2
- package/dist/client/temporal-client.service.js +1 -35
- package/dist/client/temporal-client.service.js.map +1 -1
- package/dist/{schedule → client}/temporal-schedule.service.d.ts +4 -13
- package/dist/{schedule → client}/temporal-schedule.service.js +23 -85
- package/dist/client/temporal-schedule.service.js.map +1 -0
- package/dist/constants.d.ts +0 -39
- package/dist/constants.js +1 -36
- package/dist/constants.js.map +1 -1
- package/dist/decorators/activity-method.decorator.js.map +1 -1
- package/dist/decorators/index.d.ts +2 -4
- package/dist/decorators/index.js +2 -4
- package/dist/decorators/index.js.map +1 -1
- package/dist/decorators/query-method.decorator.d.ts +1 -0
- package/dist/decorators/{query.decorator.js → query-method.decorator.js} +5 -14
- package/dist/decorators/query-method.decorator.js.map +1 -0
- package/dist/decorators/signal-method.decorator.d.ts +1 -0
- package/dist/decorators/{signal.decorator.js → signal-method.decorator.js} +5 -14
- package/dist/decorators/signal-method.decorator.js.map +1 -0
- package/dist/decorators/workflow-method.decorator.d.ts +1 -1
- package/dist/decorators/workflow-method.decorator.js +2 -5
- package/dist/decorators/workflow-method.decorator.js.map +1 -1
- package/dist/decorators/workflow.decorator.d.ts +2 -2
- package/dist/decorators/workflow.decorator.js +8 -13
- package/dist/decorators/workflow.decorator.js.map +1 -1
- package/dist/index.d.ts +5 -6
- package/dist/index.js +8 -35
- package/dist/index.js.map +1 -1
- package/dist/interfaces/activity.interface.d.ts +2 -15
- package/dist/interfaces/base.interface.d.ts +8 -19
- package/dist/interfaces/client.interface.d.ts +6 -33
- package/dist/interfaces/client.interface.js +0 -11
- package/dist/interfaces/client.interface.js.map +1 -1
- package/dist/interfaces/index.d.ts +4 -6
- package/dist/interfaces/index.js +2 -10
- package/dist/interfaces/index.js.map +1 -1
- package/dist/interfaces/temporal.interface.d.ts +33 -0
- package/dist/interfaces/{schedule.interface.js → temporal.interface.js} +1 -1
- package/dist/interfaces/temporal.interface.js.map +1 -0
- package/dist/interfaces/worker.interface.d.ts +2 -33
- package/dist/interfaces/workflow.interface.d.ts +9 -20
- package/dist/temporal.module.d.ts +6 -0
- package/dist/temporal.module.js +101 -0
- package/dist/temporal.module.js.map +1 -0
- package/dist/temporal.service.d.ts +26 -0
- package/dist/temporal.service.js +64 -0
- package/dist/temporal.service.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/worker/index.js.map +1 -1
- package/dist/worker/temporal-metadata.accessor.d.ts +0 -10
- package/dist/worker/temporal-metadata.accessor.js +0 -60
- package/dist/worker/temporal-metadata.accessor.js.map +1 -1
- package/dist/worker/temporal-worker.module.js +2 -4
- package/dist/worker/temporal-worker.module.js.map +1 -1
- package/dist/worker/worker-manager.service.d.ts +1 -1
- package/dist/worker/worker-manager.service.js +25 -58
- package/dist/worker/worker-manager.service.js.map +1 -1
- package/package.json +1 -1
- package/dist/decorators/query.decorator.d.ts +0 -2
- package/dist/decorators/query.decorator.js.map +0 -1
- package/dist/decorators/scheduled-workflow.decorator.d.ts +0 -13
- package/dist/decorators/scheduled-workflow.decorator.js +0 -31
- package/dist/decorators/scheduled-workflow.decorator.js.map +0 -1
- package/dist/decorators/signal.decorator.d.ts +0 -2
- package/dist/decorators/signal.decorator.js.map +0 -1
- package/dist/decorators/update.decorator.d.ts +0 -2
- package/dist/decorators/update.decorator.js +0 -34
- package/dist/decorators/update.decorator.js.map +0 -1
- package/dist/interfaces/schedule.interface.d.ts +0 -40
- package/dist/interfaces/schedule.interface.js.map +0 -1
- package/dist/schedule/index.d.ts +0 -2
- package/dist/schedule/index.js +0 -19
- package/dist/schedule/index.js.map +0 -1
- package/dist/schedule/temporal-schedule.module.d.ts +0 -7
- package/dist/schedule/temporal-schedule.module.js +0 -94
- package/dist/schedule/temporal-schedule.module.js.map +0 -1
- package/dist/schedule/temporal-schedule.service.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
# NestJS Temporal Core
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A simplified NestJS integration for [Temporal.io](https://temporal.io/) that provides seamless worker and client support for building reliable distributed applications.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
NestJS Temporal Core
|
|
7
|
+
NestJS Temporal Core makes it easy to integrate Temporal.io with your NestJS applications. Temporal is a durable execution system for reliable microservices and workflow orchestration.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
- 🚀 **Easy NestJS Integration** -
|
|
11
|
+
- 🚀 **Easy NestJS Integration** - Simple module registration with unified configuration
|
|
12
12
|
- 🔄 **Complete Lifecycle Management** - Automatic worker initialization and graceful shutdown
|
|
13
|
-
- 🎯 **Declarative Decorators** - Type-safe `@Activity()`, `@
|
|
14
|
-
- 🔌 **Connection Management** -
|
|
15
|
-
- 🔒 **Type Safety** -
|
|
16
|
-
- 📡 **Client
|
|
17
|
-
- 📊 **
|
|
18
|
-
- 📅 **Scheduling** -
|
|
19
|
-
- 🔍 **Queries and Signals** - First-class support for Temporal's query and signal patterns
|
|
20
|
-
- 🆕 **Updates** - Support for Temporal's workflow update functionality
|
|
21
|
-
- 🔢 **Versioning** - Support for Temporal worker versioning
|
|
22
|
-
- 🚦 **Advanced Policies** - Built-in retry, timeout, and workflow management policies
|
|
13
|
+
- 🎯 **Declarative Decorators** - Type-safe `@Activity()`, `@ActivityMethod()`, `@Workflow()`, and more
|
|
14
|
+
- 🔌 **Connection Management** - Simplified connection handling with TLS support
|
|
15
|
+
- 🔒 **Type Safety** - Clean, strongly typed interfaces for all Temporal concepts
|
|
16
|
+
- 📡 **Client Utilities** - Methods for starting, signaling, and querying workflows
|
|
17
|
+
- 📊 **Worker Management** - Simple worker lifecycle control and monitoring
|
|
18
|
+
- 📅 **Scheduling** - Support for cron and interval-based workflow scheduling
|
|
23
19
|
|
|
24
20
|
## Installation
|
|
25
21
|
|
|
@@ -42,32 +38,27 @@ async function bootstrap() {
|
|
|
42
38
|
bootstrap();
|
|
43
39
|
```
|
|
44
40
|
|
|
45
|
-
### 2. Register the
|
|
41
|
+
### 2. Register the Module
|
|
46
42
|
|
|
47
43
|
```typescript
|
|
48
44
|
import { Module } from '@nestjs/common';
|
|
49
|
-
import {
|
|
45
|
+
import { TemporalModule } from 'nestjs-temporal-core';
|
|
46
|
+
import { EmailActivities } from './activities/email.activities';
|
|
50
47
|
|
|
51
48
|
@Module({
|
|
52
49
|
imports: [
|
|
53
|
-
|
|
50
|
+
TemporalModule.register({
|
|
54
51
|
connection: {
|
|
55
52
|
address: 'localhost:7233',
|
|
53
|
+
namespace: 'default',
|
|
56
54
|
},
|
|
57
|
-
namespace: 'default',
|
|
58
55
|
taskQueue: 'my-task-queue',
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
delayMs: 1000, // Start worker after 1 second
|
|
56
|
+
worker: {
|
|
57
|
+
workflowsPath: './dist/workflows',
|
|
58
|
+
activityClasses: [EmailActivities],
|
|
59
|
+
autoStart: true,
|
|
64
60
|
},
|
|
65
|
-
|
|
66
|
-
TemporalClientModule.register({
|
|
67
|
-
connection: {
|
|
68
|
-
address: 'localhost:7233',
|
|
69
|
-
},
|
|
70
|
-
namespace: 'default',
|
|
61
|
+
isGlobal: true,
|
|
71
62
|
}),
|
|
72
63
|
],
|
|
73
64
|
})
|
|
@@ -79,25 +70,19 @@ export class AppModule {}
|
|
|
79
70
|
```typescript
|
|
80
71
|
import { Activity, ActivityMethod } from 'nestjs-temporal-core';
|
|
81
72
|
|
|
82
|
-
@Activity(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
export class PaymentActivity {
|
|
87
|
-
@ActivityMethod({
|
|
88
|
-
name: 'processPayment', // Optional custom name
|
|
89
|
-
timeout: {
|
|
90
|
-
startToClose: '30s',
|
|
91
|
-
},
|
|
92
|
-
})
|
|
93
|
-
async processPayment(amount: number): Promise<string> {
|
|
73
|
+
@Activity()
|
|
74
|
+
export class EmailActivities {
|
|
75
|
+
@ActivityMethod()
|
|
76
|
+
async sendWelcomeEmail(to: string): Promise<boolean> {
|
|
94
77
|
// Implementation
|
|
95
|
-
|
|
78
|
+
console.log(`Sending welcome email to ${to}`);
|
|
79
|
+
return true;
|
|
96
80
|
}
|
|
97
81
|
|
|
98
|
-
@ActivityMethod()
|
|
99
|
-
async
|
|
82
|
+
@ActivityMethod('sendPromoEmail')
|
|
83
|
+
async sendPromotion(to: string, promoCode: string): Promise<boolean> {
|
|
100
84
|
// Implementation
|
|
85
|
+
console.log(`Sending promo ${promoCode} to ${to}`);
|
|
101
86
|
return true;
|
|
102
87
|
}
|
|
103
88
|
}
|
|
@@ -105,329 +90,201 @@ export class PaymentActivity {
|
|
|
105
90
|
|
|
106
91
|
### 4. Define Workflows
|
|
107
92
|
|
|
93
|
+
Create a workflow file in your workflows directory:
|
|
94
|
+
|
|
108
95
|
```typescript
|
|
96
|
+
// workflows/email-workflow.ts
|
|
109
97
|
import { proxyActivities } from '@temporalio/workflow';
|
|
110
|
-
import type { PaymentActivity } from './payment.activity';
|
|
111
98
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
99
|
+
// Activities interface
|
|
100
|
+
interface EmailActivities {
|
|
101
|
+
sendWelcomeEmail(to: string): Promise<boolean>;
|
|
102
|
+
sendPromoEmail(to: string, promoCode: string): Promise<boolean>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const activities = proxyActivities<EmailActivities>({
|
|
106
|
+
startToCloseTimeout: '30s',
|
|
115
107
|
});
|
|
116
108
|
|
|
117
|
-
export async function
|
|
118
|
-
return await
|
|
109
|
+
export async function sendWelcomeWorkflow(email: string): Promise<boolean> {
|
|
110
|
+
return await activities.sendWelcomeEmail(email);
|
|
119
111
|
}
|
|
120
112
|
|
|
121
|
-
export async function
|
|
122
|
-
return await
|
|
113
|
+
export async function sendPromoWorkflow(email: string, promoCode: string): Promise<boolean> {
|
|
114
|
+
return await activities.sendPromoEmail(email, promoCode);
|
|
123
115
|
}
|
|
124
116
|
```
|
|
125
117
|
|
|
126
|
-
### 5. Use the
|
|
118
|
+
### 5. Use the Temporal Service
|
|
127
119
|
|
|
128
120
|
```typescript
|
|
129
121
|
import { Injectable } from '@nestjs/common';
|
|
130
|
-
import {
|
|
122
|
+
import { TemporalService } from 'nestjs-temporal-core';
|
|
131
123
|
|
|
132
124
|
@Injectable()
|
|
133
|
-
export class
|
|
134
|
-
constructor(private readonly
|
|
135
|
-
|
|
136
|
-
async
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
125
|
+
export class EmailService {
|
|
126
|
+
constructor(private readonly temporalService: TemporalService) {}
|
|
127
|
+
|
|
128
|
+
async sendWelcomeEmail(email: string): Promise<string> {
|
|
129
|
+
// Use the simplified API to start a workflow
|
|
130
|
+
const { workflowId } = await this.temporalService.startWorkflow(
|
|
131
|
+
'sendWelcomeWorkflow',
|
|
132
|
+
[email],
|
|
133
|
+
'my-task-queue',
|
|
140
134
|
{
|
|
141
|
-
|
|
142
|
-
workflowExecutionTimeout: '1h',
|
|
143
|
-
workflowTaskTimeout: '10s',
|
|
144
|
-
retry: {
|
|
145
|
-
maximumAttempts: 3,
|
|
146
|
-
},
|
|
135
|
+
workflowId: `welcome-${email}-${Date.now()}`,
|
|
147
136
|
},
|
|
148
137
|
);
|
|
149
138
|
|
|
150
|
-
|
|
151
|
-
const paymentId = await result;
|
|
152
|
-
|
|
153
|
-
return { paymentId, workflowId };
|
|
139
|
+
return workflowId;
|
|
154
140
|
}
|
|
155
141
|
|
|
156
|
-
async
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
142
|
+
async sendPromoEmail(email: string, promoCode: string): Promise<string> {
|
|
143
|
+
// Use the client service directly for more options
|
|
144
|
+
const { workflowId } = await this.temporalService
|
|
145
|
+
.getClient()
|
|
146
|
+
.startWorkflow('sendPromoWorkflow', [email, promoCode], {
|
|
147
|
+
taskQueue: 'my-task-queue',
|
|
148
|
+
workflowId: `promo-${email}-${Date.now()}`,
|
|
149
|
+
retry: {
|
|
150
|
+
maximumAttempts: 3,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
160
153
|
|
|
161
|
-
|
|
162
|
-
// Signal a running workflow
|
|
163
|
-
await this.temporalClient.signalWorkflow(workflowId, 'cancelPayment', [reason]);
|
|
154
|
+
return workflowId;
|
|
164
155
|
}
|
|
165
156
|
}
|
|
166
157
|
```
|
|
167
158
|
|
|
168
159
|
## Advanced Features
|
|
169
160
|
|
|
170
|
-
###
|
|
161
|
+
### Using Signals and Queries
|
|
171
162
|
|
|
172
163
|
```typescript
|
|
173
|
-
import {
|
|
174
|
-
|
|
175
|
-
@Workflow({
|
|
176
|
-
taskQueue: 'order-queue',
|
|
177
|
-
workflowExecutionTimeout: '24h',
|
|
178
|
-
})
|
|
179
|
-
export class OrderWorkflow {
|
|
180
|
-
private orderStatus: string = 'PENDING';
|
|
181
|
-
private orderItems: string[] = [];
|
|
182
|
-
private cancelReason: string | null = null;
|
|
183
|
-
|
|
184
|
-
@WorkflowMethod()
|
|
185
|
-
async execute(orderId: string): Promise<string> {
|
|
186
|
-
// Workflow implementation
|
|
187
|
-
return this.orderStatus;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
@Query()
|
|
191
|
-
getStatus(): string {
|
|
192
|
-
return this.orderStatus;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
@Signal()
|
|
196
|
-
cancel(reason: string): void {
|
|
197
|
-
this.orderStatus = 'CANCELLED';
|
|
198
|
-
this.cancelReason = reason;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
@Update({
|
|
202
|
-
validator: (items: string[]) => {
|
|
203
|
-
if (items.length === 0) {
|
|
204
|
-
throw new Error('Order must contain at least one item');
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
})
|
|
208
|
-
updateItems(items: string[]): string[] {
|
|
209
|
-
this.orderItems = items;
|
|
210
|
-
return this.orderItems;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Scheduled Workflows
|
|
164
|
+
import { Injectable } from '@nestjs/common';
|
|
165
|
+
import { TemporalService } from 'nestjs-temporal-core';
|
|
216
166
|
|
|
217
|
-
|
|
218
|
-
|
|
167
|
+
@Injectable()
|
|
168
|
+
export class OrderService {
|
|
169
|
+
constructor(private readonly temporalService: TemporalService) {}
|
|
219
170
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
cron: '0 0 * * *', // Run daily at midnight
|
|
224
|
-
},
|
|
225
|
-
description: 'Daily reporting workflow',
|
|
226
|
-
})
|
|
227
|
-
export class DailyReportWorkflow {
|
|
228
|
-
@WorkflowMethod()
|
|
229
|
-
async execute(): Promise<void> {
|
|
230
|
-
// Generate and send reports
|
|
171
|
+
async addItemToOrder(orderId: string, item: string): Promise<void> {
|
|
172
|
+
// Signal a running workflow
|
|
173
|
+
await this.temporalService.getClient().signalWorkflow(orderId, 'addItem', [item]);
|
|
231
174
|
}
|
|
232
|
-
}
|
|
233
175
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
interval: {
|
|
238
|
-
hours: 1, // Run every hour
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
description: 'Hourly data processing',
|
|
242
|
-
})
|
|
243
|
-
export class HourlyProcessingWorkflow {
|
|
244
|
-
@WorkflowMethod()
|
|
245
|
-
async execute(): Promise<void> {
|
|
246
|
-
// Process data hourly
|
|
176
|
+
async getOrderStatus(orderId: string): Promise<string> {
|
|
177
|
+
// Query a running workflow
|
|
178
|
+
return await this.temporalService.getClient().queryWorkflow(orderId, 'getStatus');
|
|
247
179
|
}
|
|
248
180
|
}
|
|
249
181
|
```
|
|
250
182
|
|
|
251
|
-
###
|
|
183
|
+
### Schedule Management
|
|
252
184
|
|
|
253
185
|
```typescript
|
|
254
186
|
import { Injectable } from '@nestjs/common';
|
|
255
|
-
import {
|
|
187
|
+
import { TemporalService } from 'nestjs-temporal-core';
|
|
256
188
|
|
|
257
189
|
@Injectable()
|
|
258
190
|
export class ReportingService {
|
|
259
|
-
constructor(private readonly
|
|
191
|
+
constructor(private readonly temporalService: TemporalService) {}
|
|
260
192
|
|
|
261
|
-
async
|
|
262
|
-
|
|
263
|
-
'daily-
|
|
264
|
-
'
|
|
193
|
+
async scheduleDailyReport(): Promise<string> {
|
|
194
|
+
const handle = await this.temporalService.getScheduleService().createCronWorkflow(
|
|
195
|
+
'daily-report',
|
|
196
|
+
'generateReportWorkflow',
|
|
265
197
|
'0 0 * * *', // Daily at midnight
|
|
266
198
|
'reports-queue',
|
|
267
|
-
[],
|
|
268
|
-
'Daily sales report generation',
|
|
199
|
+
['daily-summary'],
|
|
269
200
|
);
|
|
270
|
-
}
|
|
271
201
|
|
|
272
|
-
|
|
273
|
-
return await this.scheduleService.createIntervalWorkflow(
|
|
274
|
-
'hourly-data-backup',
|
|
275
|
-
'backupData',
|
|
276
|
-
{ hours: 1 },
|
|
277
|
-
'backup-queue',
|
|
278
|
-
[],
|
|
279
|
-
'Hourly data backup process',
|
|
280
|
-
);
|
|
202
|
+
return handle.scheduleId;
|
|
281
203
|
}
|
|
282
204
|
|
|
283
|
-
async
|
|
284
|
-
await this.
|
|
285
|
-
|
|
286
|
-
'Paused for
|
|
287
|
-
);
|
|
205
|
+
async pauseReporting(): Promise<void> {
|
|
206
|
+
await this.temporalService
|
|
207
|
+
.getScheduleService()
|
|
208
|
+
.pauseSchedule('daily-report', 'Paused for maintenance');
|
|
288
209
|
}
|
|
289
210
|
|
|
290
|
-
async
|
|
291
|
-
|
|
211
|
+
async resumeReporting(): Promise<void> {
|
|
212
|
+
await this.temporalService.getScheduleService().resumeSchedule('daily-report');
|
|
292
213
|
}
|
|
293
214
|
}
|
|
294
215
|
```
|
|
295
216
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
import { Injectable } from '@nestjs/common';
|
|
300
|
-
import { TemporalClientService } from 'nestjs-temporal-core';
|
|
301
|
-
|
|
302
|
-
@Injectable()
|
|
303
|
-
export class OrderService {
|
|
304
|
-
constructor(private readonly temporalClient: TemporalClientService) {}
|
|
305
|
-
|
|
306
|
-
async updateOrderItems(orderId: string, items: string[]) {
|
|
307
|
-
try {
|
|
308
|
-
// Use the updateWorkflow method to modify a running workflow
|
|
309
|
-
const updatedItems = await this.temporalClient.updateWorkflow<string[]>(
|
|
310
|
-
`order-${orderId}`,
|
|
311
|
-
'updateItems',
|
|
312
|
-
[items],
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
return { success: true, items: updatedItems };
|
|
316
|
-
} catch (error) {
|
|
317
|
-
return { success: false, error: error.message };
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
## Advanced Configuration
|
|
217
|
+
## Configuration Options
|
|
324
218
|
|
|
325
219
|
### Async Configuration
|
|
326
220
|
|
|
327
|
-
Use factory patterns for dynamic configuration:
|
|
328
|
-
|
|
329
221
|
```typescript
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
address: configService.get('TEMPORAL_ADDRESS'),
|
|
335
|
-
connectionTimeout: configService.get('TEMPORAL_CONNECTION_TIMEOUT', 5000),
|
|
336
|
-
},
|
|
337
|
-
namespace: configService.get('TEMPORAL_NAMESPACE'),
|
|
338
|
-
taskQueue: configService.get('TEMPORAL_TASK_QUEUE'),
|
|
339
|
-
workflowsPath: require.resolve('./workflows'),
|
|
340
|
-
activityClasses: [MyActivity],
|
|
341
|
-
autoStart: {
|
|
342
|
-
enabled: configService.get('TEMPORAL_WORKER_AUTOSTART', true),
|
|
343
|
-
delayMs: configService.get('TEMPORAL_WORKER_START_DELAY', 0),
|
|
344
|
-
},
|
|
345
|
-
allowWorkerFailure: configService.get('TEMPORAL_ALLOW_WORKER_FAILURE', true),
|
|
346
|
-
}),
|
|
347
|
-
inject: [ConfigService],
|
|
348
|
-
});
|
|
349
|
-
```
|
|
222
|
+
import { Module } from '@nestjs/common';
|
|
223
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
224
|
+
import { TemporalModule } from 'nestjs-temporal-core';
|
|
225
|
+
import { EmailActivities } from './activities/email.activities';
|
|
350
226
|
|
|
351
|
-
|
|
227
|
+
@Module({
|
|
228
|
+
imports: [
|
|
229
|
+
ConfigModule.forRoot(),
|
|
230
|
+
TemporalModule.registerAsync({
|
|
231
|
+
imports: [ConfigModule],
|
|
232
|
+
inject: [ConfigService],
|
|
233
|
+
useFactory: (configService: ConfigService) => ({
|
|
234
|
+
connection: {
|
|
235
|
+
address: configService.get('TEMPORAL_ADDRESS'),
|
|
236
|
+
namespace: configService.get('TEMPORAL_NAMESPACE'),
|
|
237
|
+
apiKey: configService.get('TEMPORAL_API_KEY'),
|
|
238
|
+
},
|
|
239
|
+
taskQueue: configService.get('TEMPORAL_TASK_QUEUE'),
|
|
240
|
+
worker: {
|
|
241
|
+
workflowsPath: './dist/workflows',
|
|
242
|
+
activityClasses: [EmailActivities],
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
isGlobal: true,
|
|
246
|
+
}),
|
|
247
|
+
}),
|
|
248
|
+
],
|
|
249
|
+
})
|
|
250
|
+
export class AppModule {}
|
|
251
|
+
```
|
|
352
252
|
|
|
353
|
-
|
|
253
|
+
### Secure Connection with TLS
|
|
354
254
|
|
|
355
255
|
```typescript
|
|
356
|
-
|
|
256
|
+
TemporalModule.register({
|
|
357
257
|
connection: {
|
|
358
258
|
address: 'temporal.example.com:7233',
|
|
259
|
+
namespace: 'production',
|
|
359
260
|
tls: {
|
|
261
|
+
// Simple boolean for default TLS
|
|
262
|
+
// tls: true,
|
|
263
|
+
|
|
264
|
+
// Or detailed configuration
|
|
265
|
+
serverName: 'temporal.example.com',
|
|
360
266
|
clientCertPair: {
|
|
361
|
-
crt:
|
|
362
|
-
key:
|
|
363
|
-
ca:
|
|
267
|
+
crt: fs.readFileSync('./certs/client.crt'),
|
|
268
|
+
key: fs.readFileSync('./certs/client.key'),
|
|
269
|
+
ca: fs.readFileSync('./certs/ca.crt'),
|
|
364
270
|
},
|
|
365
|
-
serverName: 'temporal.example.com', // Optional for SNI
|
|
366
|
-
verifyServer: true, // Optional, defaults to true
|
|
367
271
|
},
|
|
368
|
-
|
|
272
|
+
// For Temporal Cloud
|
|
273
|
+
apiKey: process.env.TEMPORAL_API_KEY,
|
|
369
274
|
},
|
|
370
|
-
|
|
371
|
-
allowConnectionFailure: true, // Allow application to start if Temporal connection fails
|
|
372
|
-
});
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
### Worker Versioning
|
|
376
|
-
|
|
377
|
-
Enable worker versioning for compatibility management:
|
|
378
|
-
|
|
379
|
-
```typescript
|
|
380
|
-
TemporalWorkerModule.register({
|
|
381
|
-
// ... other options
|
|
382
|
-
useVersioning: true,
|
|
383
|
-
buildId: 'v1.2.3-20230615', // Unique identifier for this worker's code version
|
|
384
|
-
});
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### Worker Performance Tuning
|
|
388
|
-
|
|
389
|
-
Optimize worker performance with advanced options:
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
TemporalWorkerModule.register({
|
|
393
|
-
// ... other options
|
|
394
|
-
reuseV8Context: true, // Significantly improves performance and reduces memory usage
|
|
395
|
-
maxConcurrentWorkflowTaskExecutions: 40,
|
|
396
|
-
maxConcurrentActivityTaskExecutions: 100,
|
|
397
|
-
maxConcurrentLocalActivityExecutions: 100,
|
|
398
|
-
workflowThreadPoolSize: 4,
|
|
399
|
-
maxActivitiesPerSecond: 50,
|
|
400
|
-
maxCachedWorkflows: 200,
|
|
275
|
+
// ...other options
|
|
401
276
|
});
|
|
402
277
|
```
|
|
403
278
|
|
|
404
|
-
### Worker Monitoring
|
|
405
|
-
|
|
406
|
-
Enable worker monitoring and metrics:
|
|
407
|
-
|
|
408
|
-
```typescript
|
|
409
|
-
TemporalWorkerModule.register({
|
|
410
|
-
// ... other options
|
|
411
|
-
monitoring: {
|
|
412
|
-
statsIntervalMs: 60000, // Log stats every minute
|
|
413
|
-
metrics: {
|
|
414
|
-
enabled: true,
|
|
415
|
-
prometheus: {
|
|
416
|
-
enabled: true,
|
|
417
|
-
port: 9464,
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
});
|
|
422
279
|
```
|
|
423
280
|
|
|
424
281
|
## API Reference
|
|
425
282
|
|
|
426
283
|
### Core Modules
|
|
427
284
|
|
|
428
|
-
- `
|
|
429
|
-
- `
|
|
430
|
-
- `
|
|
285
|
+
- `TemporalModule` - Unified module for both client and worker functionality
|
|
286
|
+
- `TemporalClientModule` - Client-only module for workflow operations
|
|
287
|
+
- `TemporalWorkerModule` - Worker-only module for running activities
|
|
431
288
|
|
|
432
289
|
### Decorators
|
|
433
290
|
|
|
@@ -435,33 +292,35 @@ TemporalWorkerModule.register({
|
|
|
435
292
|
- `@ActivityMethod(options?)` - Marks a method as an activity implementation
|
|
436
293
|
- `@Workflow(options)` - Marks a class as a Temporal workflow
|
|
437
294
|
- `@WorkflowMethod(options?)` - Marks the primary workflow execution method
|
|
438
|
-
- `@
|
|
439
|
-
- `@
|
|
440
|
-
- `@Update(options?)` - Marks a method as an update handler
|
|
441
|
-
- `@ScheduledWorkflow(options)` - Defines a workflow with schedule configuration
|
|
295
|
+
- `@SignalMethod(name?)` - Marks a method as a signal handler
|
|
296
|
+
- `@QueryMethod(name?)` - Marks a method as a query handler
|
|
442
297
|
|
|
443
298
|
### Services
|
|
444
299
|
|
|
300
|
+
#### TemporalService
|
|
301
|
+
|
|
302
|
+
- `getClient()` - Get the client service
|
|
303
|
+
- `getScheduleService()` - Get the schedule service
|
|
304
|
+
- `getWorkerManager()` - Get the worker manager (if available)
|
|
305
|
+
- `startWorkflow()` - Simplified method to start a workflow
|
|
306
|
+
- `hasWorker()` - Check if worker functionality is available
|
|
307
|
+
|
|
308
|
+
|
|
445
309
|
#### TemporalClientService
|
|
446
310
|
|
|
447
|
-
- `startWorkflow
|
|
311
|
+
- `startWorkflow()` - Start a new workflow execution
|
|
448
312
|
- `signalWorkflow()` - Send a signal to a running workflow
|
|
449
|
-
- `queryWorkflow
|
|
450
|
-
- `updateWorkflow<T>()` - Update a running workflow
|
|
313
|
+
- `queryWorkflow()` - Query a running workflow
|
|
451
314
|
- `terminateWorkflow()` - Terminate a running workflow
|
|
452
315
|
- `cancelWorkflow()` - Request cancellation of a workflow
|
|
453
316
|
- `getWorkflowHandle()` - Get a handle to manage a workflow
|
|
454
317
|
- `describeWorkflow()` - Get workflow execution details
|
|
455
318
|
- `listWorkflows()` - List workflows matching a query
|
|
456
|
-
- `countWorkflows()` - Count workflows matching a query
|
|
457
|
-
- `getWorkflowClient()` - Get the underlying workflow client
|
|
458
|
-
- `getRawClient()` - Get the raw Temporal client
|
|
459
319
|
|
|
460
320
|
#### TemporalScheduleService
|
|
461
321
|
|
|
462
322
|
- `createCronWorkflow()` - Create a workflow scheduled by cron expression
|
|
463
323
|
- `createIntervalWorkflow()` - Create a workflow scheduled by time interval
|
|
464
|
-
- `getSchedule()` - Get a handle to an existing schedule
|
|
465
324
|
- `pauseSchedule()` - Pause a schedule
|
|
466
325
|
- `resumeSchedule()` - Resume a paused schedule
|
|
467
326
|
- `deleteSchedule()` - Delete a schedule
|
|
@@ -474,64 +333,314 @@ TemporalWorkerModule.register({
|
|
|
474
333
|
- `shutdown()` - Gracefully shutdown the worker
|
|
475
334
|
- `getWorker()` - Get the underlying worker instance
|
|
476
335
|
|
|
477
|
-
##
|
|
336
|
+
## Project Structure
|
|
337
|
+
|
|
338
|
+
When integrating Temporal with your NestJS application, organizing your code properly helps maintain separation of concerns and follows NestJS conventions. Here's a recommended project structure:
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
src/
|
|
343
|
+
├── temporal/
|
|
344
|
+
│ ├── activities/
|
|
345
|
+
│ │ ├── email.activities.ts
|
|
346
|
+
│ │ ├── payment.activities.ts
|
|
347
|
+
│ │ └── index.ts
|
|
348
|
+
│ ├── workflows/
|
|
349
|
+
│ │ ├── email-workflows.ts
|
|
350
|
+
│ │ ├── payment-workflows.ts
|
|
351
|
+
│ │ └── index.ts
|
|
352
|
+
│ ├── interfaces/
|
|
353
|
+
│ │ ├── email.interfaces.ts
|
|
354
|
+
│ │ └── payment.interfaces.ts
|
|
355
|
+
│ └── temporal.module.ts
|
|
356
|
+
├── modules/
|
|
357
|
+
│ ├── email/
|
|
358
|
+
│ │ ├── email.service.ts
|
|
359
|
+
│ │ ├── email.controller.ts
|
|
360
|
+
│ │ └── email.module.ts
|
|
361
|
+
│ └── payment/
|
|
362
|
+
│ ├── payment.service.ts
|
|
363
|
+
│ ├── payment.controller.ts
|
|
364
|
+
│ └── payment.module.ts
|
|
365
|
+
└── app.module.ts
|
|
366
|
+
|
|
367
|
+
````
|
|
368
|
+
|
|
369
|
+
### Key Files and Their Purpose
|
|
370
|
+
|
|
371
|
+
1. **Activities (src/temporal/activities/)**:
|
|
372
|
+
- Contains activity classes decorated with `@Activity()`
|
|
373
|
+
- Each activity class should group related functionality
|
|
374
|
+
|
|
375
|
+
2. **Workflows (src/temporal/workflows/)**:
|
|
376
|
+
- Contains workflow definitions that orchestrate activities
|
|
377
|
+
- Workflows should be in separate files based on domain
|
|
378
|
+
|
|
379
|
+
3. **Interfaces (src/temporal/interfaces/)**:
|
|
380
|
+
- TypeScript interfaces that define activity and workflow parameters/returns
|
|
381
|
+
- Helps maintain type safety between activities and workflows
|
|
382
|
+
|
|
383
|
+
4. **Temporal Module (src/temporal/temporal.module.ts)**:
|
|
384
|
+
- Centralizes Temporal configuration
|
|
385
|
+
- Imports and registers all activities
|
|
386
|
+
|
|
387
|
+
5. **Business Services (src/modules/*/)**:
|
|
388
|
+
- Inject the TemporalService
|
|
389
|
+
- Use it to start workflows and interact with Temporal
|
|
390
|
+
|
|
391
|
+
## Integration Examples
|
|
392
|
+
|
|
393
|
+
### Activity Definition
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
// src/temporal/activities/email.activities.ts
|
|
397
|
+
import { Activity, ActivityMethod } from 'nestjs-temporal-core';
|
|
398
|
+
import { Injectable } from '@nestjs/common';
|
|
399
|
+
import { EmailService } from '../../modules/email/email.service';
|
|
400
|
+
|
|
401
|
+
@Injectable()
|
|
402
|
+
@Activity()
|
|
403
|
+
export class EmailActivities {
|
|
404
|
+
constructor(private readonly emailService: EmailService) {}
|
|
405
|
+
|
|
406
|
+
@ActivityMethod()
|
|
407
|
+
async sendWelcomeEmail(to: string, name: string): Promise<boolean> {
|
|
408
|
+
await this.emailService.sendEmail({
|
|
409
|
+
to,
|
|
410
|
+
subject: 'Welcome!',
|
|
411
|
+
body: `Hello ${name}, welcome to our platform!`,
|
|
412
|
+
});
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
@ActivityMethod()
|
|
417
|
+
async sendPasswordReset(to: string, resetToken: string): Promise<boolean> {
|
|
418
|
+
await this.emailService.sendEmail({
|
|
419
|
+
to,
|
|
420
|
+
subject: 'Password Reset',
|
|
421
|
+
body: `Please use this token to reset your password: ${resetToken}`,
|
|
422
|
+
});
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
````
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
// src/temporal/activities/index.ts
|
|
430
|
+
export * from './email.activities';
|
|
431
|
+
export * from './payment.activities';
|
|
432
|
+
// Export all other activity classes
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Workflow Definition
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
// src/temporal/workflows/email-workflows.ts
|
|
439
|
+
import { proxyActivities } from '@temporalio/workflow';
|
|
440
|
+
|
|
441
|
+
// Define the interface of activities this workflow will use
|
|
442
|
+
export interface EmailActivities {
|
|
443
|
+
sendWelcomeEmail(to: string, name: string): Promise<boolean>;
|
|
444
|
+
sendPasswordReset(to: string, resetToken: string): Promise<boolean>;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Create a proxy to the activities
|
|
448
|
+
const activities = proxyActivities<EmailActivities>({
|
|
449
|
+
startToCloseTimeout: '30 seconds',
|
|
450
|
+
});
|
|
478
451
|
|
|
479
|
-
|
|
452
|
+
// Welcome workflow with retry logic
|
|
453
|
+
export async function welcomeUserWorkflow(email: string, name: string): Promise<boolean> {
|
|
454
|
+
let attempts = 0;
|
|
455
|
+
const maxAttempts = 3;
|
|
456
|
+
|
|
457
|
+
while (attempts < maxAttempts) {
|
|
458
|
+
try {
|
|
459
|
+
return await activities.sendWelcomeEmail(email, name);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
attempts++;
|
|
462
|
+
if (attempts >= maxAttempts) {
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
// Wait before retrying
|
|
466
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
480
469
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Password reset workflow
|
|
474
|
+
export async function passwordResetWorkflow(email: string, resetToken: string): Promise<boolean> {
|
|
475
|
+
return await activities.sendPasswordReset(email, resetToken);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Scheduled Workflow Example
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
// src/temporal/workflows/scheduled-workflows.ts
|
|
483
|
+
import { proxyActivities } from '@temporalio/workflow';
|
|
484
|
+
|
|
485
|
+
interface ReportActivities {
|
|
486
|
+
generateDailyReport(): Promise<string>;
|
|
487
|
+
emailReportToAdmins(reportUrl: string): Promise<boolean>;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const activities = proxyActivities<ReportActivities>({
|
|
491
|
+
startToCloseTimeout: '10 minutes',
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// This workflow will be scheduled to run daily
|
|
495
|
+
export async function dailyReportWorkflow(): Promise<void> {
|
|
496
|
+
const reportUrl = await activities.generateDailyReport();
|
|
497
|
+
await activities.emailReportToAdmins(reportUrl);
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Setting Up the Temporal Module
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
// src/temporal/temporal.module.ts
|
|
505
|
+
import { Module } from '@nestjs/common';
|
|
506
|
+
import { TemporalModule } from 'nestjs-temporal-core';
|
|
507
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
508
|
+
import * as path from 'path';
|
|
509
|
+
import { EmailActivities, PaymentActivities } from './activities';
|
|
510
|
+
import { EmailModule } from '../modules/email/email.module';
|
|
511
|
+
import { PaymentModule } from '../modules/payment/payment.module';
|
|
512
|
+
|
|
513
|
+
@Module({
|
|
514
|
+
imports: [
|
|
515
|
+
EmailModule,
|
|
516
|
+
PaymentModule,
|
|
517
|
+
TemporalModule.registerAsync({
|
|
518
|
+
imports: [ConfigModule],
|
|
519
|
+
inject: [ConfigService],
|
|
520
|
+
useFactory: (configService: ConfigService) => ({
|
|
521
|
+
connection: {
|
|
522
|
+
address: configService.get('TEMPORAL_ADDRESS', 'localhost:7233'),
|
|
523
|
+
namespace: configService.get('TEMPORAL_NAMESPACE', 'default'),
|
|
524
|
+
},
|
|
525
|
+
taskQueue: configService.get('TEMPORAL_TASK_QUEUE', 'my-app-queue'),
|
|
526
|
+
worker: {
|
|
527
|
+
workflowsPath: path.resolve(__dirname, 'workflows'),
|
|
528
|
+
activityClasses: [EmailActivities, PaymentActivities],
|
|
529
|
+
autoStart: true,
|
|
530
|
+
},
|
|
531
|
+
isGlobal: true,
|
|
532
|
+
}),
|
|
533
|
+
}),
|
|
534
|
+
],
|
|
535
|
+
providers: [EmailActivities, PaymentActivities],
|
|
536
|
+
exports: [TemporalModule],
|
|
537
|
+
})
|
|
538
|
+
export class TemporalAppModule {}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Using Temporal in Business Services
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
// src/modules/email/email.service.ts
|
|
545
|
+
import { Injectable } from '@nestjs/common';
|
|
546
|
+
import { TemporalService } from 'nestjs-temporal-core';
|
|
547
|
+
|
|
548
|
+
@Injectable()
|
|
549
|
+
export class EmailService {
|
|
550
|
+
constructor(private readonly temporalService: TemporalService) {}
|
|
551
|
+
|
|
552
|
+
// Your actual email sending functionality
|
|
553
|
+
async sendEmail(options: { to: string; subject: string; body: string }): Promise<void> {
|
|
554
|
+
// Implementation...
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Triggering a Temporal workflow
|
|
558
|
+
async sendWelcomeEmail(email: string, name: string): Promise<string> {
|
|
559
|
+
const { workflowId } = await this.temporalService.startWorkflow(
|
|
560
|
+
'welcomeUserWorkflow',
|
|
561
|
+
[email, name],
|
|
562
|
+
'my-app-queue',
|
|
563
|
+
{ workflowId: `welcome-${email}-${Date.now()}` },
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
return workflowId;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Setting Up Scheduled Workflows
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
// src/modules/reports/report.service.ts
|
|
575
|
+
import { Injectable, OnModuleInit } from '@nestjs/common';
|
|
576
|
+
import { TemporalService } from 'nestjs-temporal-core';
|
|
577
|
+
|
|
578
|
+
@Injectable()
|
|
579
|
+
export class ReportService implements OnModuleInit {
|
|
580
|
+
constructor(private readonly temporalService: TemporalService) {}
|
|
581
|
+
|
|
582
|
+
async onModuleInit() {
|
|
583
|
+
// Set up scheduled workflows when the module initializes
|
|
584
|
+
await this.setupDailyReport();
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private async setupDailyReport() {
|
|
588
|
+
try {
|
|
589
|
+
// Check if the schedule already exists
|
|
590
|
+
const schedules = await this.temporalService.getScheduleService().listSchedules();
|
|
591
|
+
|
|
592
|
+
if (!schedules.some((s) => s.scheduleId === 'daily-report')) {
|
|
593
|
+
// Create a new schedule
|
|
594
|
+
await this.temporalService.getScheduleService().createCronWorkflow(
|
|
595
|
+
'daily-report',
|
|
596
|
+
'dailyReportWorkflow',
|
|
597
|
+
'0 8 * * *', // Run every day at 8 AM
|
|
598
|
+
'my-app-queue',
|
|
599
|
+
[], // No arguments needed for this workflow
|
|
600
|
+
'Daily business report generation',
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
console.log('Daily report schedule created');
|
|
604
|
+
}
|
|
605
|
+
} catch (error) {
|
|
606
|
+
console.error('Failed to set up daily report schedule:', error);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
```
|
|
487
611
|
|
|
488
612
|
## Best Practices
|
|
489
613
|
|
|
490
614
|
1. **Activity Design**
|
|
491
615
|
|
|
492
|
-
- Define activity interfaces for type safety
|
|
493
616
|
- Keep activities focused on single responsibilities
|
|
494
617
|
- Set appropriate timeouts for expected durations
|
|
495
|
-
- Use
|
|
618
|
+
- Use dependency injection within activity classes
|
|
496
619
|
|
|
497
620
|
2. **Workflow Design**
|
|
498
621
|
|
|
499
622
|
- Make workflows deterministic
|
|
500
623
|
- Use signals for external events
|
|
501
624
|
- Use queries for retrieving workflow state
|
|
502
|
-
-
|
|
503
|
-
- Avoid side effects in workflow code
|
|
625
|
+
- Separate workflows by domain/function
|
|
504
626
|
|
|
505
627
|
3. **Configuration**
|
|
506
628
|
|
|
507
629
|
- Use meaningful workflow IDs for tracking
|
|
508
630
|
- Configure appropriate timeouts for activities and workflows
|
|
509
|
-
-
|
|
510
|
-
- Set up monitoring for production deployments
|
|
511
|
-
- Enable worker versioning in production
|
|
512
|
-
|
|
513
|
-
4. **Performance Optimization**
|
|
631
|
+
- Implement proper error handling
|
|
514
632
|
|
|
515
|
-
|
|
516
|
-
- Configure appropriate concurrency settings
|
|
517
|
-
- Use caching for frequently accessed workflows
|
|
518
|
-
- Balance worker thread pool size
|
|
519
|
-
|
|
520
|
-
5. **Lifecycle Management**
|
|
633
|
+
4. **Lifecycle Management**
|
|
521
634
|
|
|
522
635
|
- Enable NestJS shutdown hooks
|
|
523
636
|
- Configure proper worker shutdown grace periods
|
|
524
|
-
-
|
|
637
|
+
- Consider using OnModuleInit to set up schedules
|
|
525
638
|
|
|
526
|
-
|
|
639
|
+
5. **Security**
|
|
527
640
|
- Implement proper TLS security for production environments
|
|
528
641
|
- Use namespaces to isolate different environments
|
|
529
642
|
- Use API keys for Temporal Cloud authentication
|
|
530
643
|
|
|
531
|
-
## Contributing
|
|
532
|
-
|
|
533
|
-
Contributions are welcome! Please see our [contributing guidelines](CONTRIBUTING.md) for details.
|
|
534
|
-
|
|
535
644
|
## License
|
|
536
645
|
|
|
537
646
|
MIT
|