nestjs-temporal-core 3.0.3 → 3.0.5
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 +395 -177
- package/dist/activity/index.d.ts +2 -0
- package/dist/activity/index.js +19 -0
- package/dist/activity/index.js.map +1 -0
- package/dist/activity/temporal-activity.module.d.ts +11 -0
- package/dist/activity/temporal-activity.module.js +51 -0
- package/dist/activity/temporal-activity.module.js.map +1 -0
- package/dist/activity/temporal-activity.service.d.ts +46 -0
- package/dist/activity/temporal-activity.service.js +190 -0
- package/dist/activity/temporal-activity.service.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +4 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +41 -0
- package/dist/schedules/index.d.ts +2 -0
- package/dist/schedules/index.js +19 -0
- package/dist/schedules/index.js.map +1 -0
- package/dist/schedules/temporal-schedules.module.d.ts +11 -0
- package/dist/schedules/temporal-schedules.module.js +54 -0
- package/dist/schedules/temporal-schedules.module.js.map +1 -0
- package/dist/schedules/temporal-schedules.service.d.ts +52 -0
- package/dist/schedules/temporal-schedules.service.js +215 -0
- package/dist/schedules/temporal-schedules.service.js.map +1 -0
- package/dist/temporal.service.js +1 -1
- package/dist/temporal.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -4
package/README.md
CHANGED
|
@@ -10,6 +10,10 @@ A comprehensive NestJS integration for [Temporal.io](https://temporal.io/) that
|
|
|
10
10
|
|
|
11
11
|
NestJS Temporal Core brings Temporal's durable execution to NestJS with familiar decorator patterns and automatic discovery. Build reliable distributed systems with activities and scheduled tasks using native NestJS conventions.
|
|
12
12
|
|
|
13
|
+
## 💡 Example Repository
|
|
14
|
+
|
|
15
|
+
🔗 **[Complete Example Project](https://github.com/harsh-simform/nestjs-temporal-core-example)** - Check out our full working example repository to see NestJS Temporal Core in action with real-world use cases, configuration examples, and best practices.
|
|
16
|
+
|
|
13
17
|
## 🚀 Key Features
|
|
14
18
|
|
|
15
19
|
- **🎯 NestJS-Native** - Familiar patterns: `@Activity`, `@Cron`, `@Interval`
|
|
@@ -19,7 +23,8 @@ NestJS Temporal Core brings Temporal's durable execution to NestJS with familiar
|
|
|
19
23
|
- **⚙️ Flexible Setup** - Client-only, worker-only, or unified deployments
|
|
20
24
|
- **🏥 Health Monitoring** - Comprehensive status monitoring and health checks
|
|
21
25
|
- **🔧 Production Ready** - TLS, connection management, graceful shutdowns
|
|
22
|
-
- **📊
|
|
26
|
+
- **📊 Modular Architecture** - Individual modules for specific needs
|
|
27
|
+
- **🔐 Enterprise Ready** - Temporal Cloud support with TLS and API keys
|
|
23
28
|
|
|
24
29
|
## 📦 Installation
|
|
25
30
|
|
|
@@ -29,7 +34,9 @@ npm install nestjs-temporal-core @temporalio/client @temporalio/worker @temporal
|
|
|
29
34
|
|
|
30
35
|
## 🚀 Quick Start
|
|
31
36
|
|
|
32
|
-
### 1.
|
|
37
|
+
### 1. Complete Integration (Recommended)
|
|
38
|
+
|
|
39
|
+
For applications that need full Temporal functionality:
|
|
33
40
|
|
|
34
41
|
```typescript
|
|
35
42
|
// app.module.ts
|
|
@@ -44,294 +51,505 @@ import { EmailActivities } from './activities/email.activities';
|
|
|
44
51
|
address: 'localhost:7233',
|
|
45
52
|
namespace: 'default',
|
|
46
53
|
},
|
|
47
|
-
taskQueue: '
|
|
54
|
+
taskQueue: 'main-queue',
|
|
48
55
|
worker: {
|
|
49
56
|
workflowsPath: './dist/workflows',
|
|
50
|
-
activityClasses: [EmailActivities],
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
activityClasses: [EmailActivities],
|
|
58
|
+
autoStart: true
|
|
59
|
+
}
|
|
60
|
+
})
|
|
53
61
|
],
|
|
54
|
-
providers: [EmailActivities],
|
|
62
|
+
providers: [EmailActivities], // Auto-discovered
|
|
55
63
|
})
|
|
56
64
|
export class AppModule {}
|
|
57
65
|
```
|
|
58
66
|
|
|
59
|
-
### 2.
|
|
67
|
+
### 2. Define Activities
|
|
60
68
|
|
|
61
69
|
```typescript
|
|
62
70
|
// activities/email.activities.ts
|
|
63
71
|
import { Injectable } from '@nestjs/common';
|
|
64
72
|
import { Activity, ActivityMethod } from 'nestjs-temporal-core';
|
|
65
73
|
|
|
66
|
-
@Injectable()
|
|
67
74
|
@Activity()
|
|
75
|
+
@Injectable()
|
|
68
76
|
export class EmailActivities {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
|
|
78
|
+
@ActivityMethod({
|
|
79
|
+
name: 'sendEmail',
|
|
80
|
+
timeout: '30s',
|
|
81
|
+
maxRetries: 3
|
|
82
|
+
})
|
|
83
|
+
async sendEmail(to: string, subject: string, body: string): Promise<void> {
|
|
84
|
+
console.log(`Sending email to ${to}: ${subject}`);
|
|
85
|
+
// Your email sending logic here
|
|
74
86
|
}
|
|
75
87
|
|
|
76
|
-
@ActivityMethod()
|
|
77
|
-
async sendNotification(
|
|
78
|
-
console.log(`
|
|
88
|
+
@ActivityMethod('sendNotification')
|
|
89
|
+
async sendNotification(userId: string, message: string): Promise<void> {
|
|
90
|
+
console.log(`Notifying user ${userId}: ${message}`);
|
|
79
91
|
// Your notification logic here
|
|
80
92
|
}
|
|
81
93
|
}
|
|
82
94
|
```
|
|
83
95
|
|
|
84
|
-
### 3. Create
|
|
96
|
+
### 3. Create Workflows
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// workflows/email.workflow.ts
|
|
100
|
+
import { proxyActivities } from '@temporalio/workflow';
|
|
101
|
+
import type { EmailActivities } from '../activities/email.activities';
|
|
102
|
+
|
|
103
|
+
const { sendEmail, sendNotification } = proxyActivities<EmailActivities>({
|
|
104
|
+
startToCloseTimeout: '1 minute',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export async function processEmailWorkflow(
|
|
108
|
+
userId: string,
|
|
109
|
+
emailData: { to: string; subject: string; body: string }
|
|
110
|
+
): Promise<void> {
|
|
111
|
+
// Send email
|
|
112
|
+
await sendEmail(emailData.to, emailData.subject, emailData.body);
|
|
113
|
+
|
|
114
|
+
// Send notification
|
|
115
|
+
await sendNotification(userId, 'Email sent successfully');
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 4. Schedule Workflows
|
|
85
120
|
|
|
86
121
|
```typescript
|
|
87
122
|
// services/scheduled.service.ts
|
|
88
123
|
import { Injectable } from '@nestjs/common';
|
|
89
|
-
import { Cron, Interval } from 'nestjs-temporal-core';
|
|
124
|
+
import { Scheduled, Cron, Interval, CRON_EXPRESSIONS } from 'nestjs-temporal-core';
|
|
90
125
|
|
|
91
126
|
@Injectable()
|
|
92
127
|
export class ScheduledService {
|
|
93
|
-
|
|
94
|
-
@
|
|
95
|
-
scheduleId: 'daily-
|
|
96
|
-
|
|
128
|
+
|
|
129
|
+
@Scheduled({
|
|
130
|
+
scheduleId: 'daily-report',
|
|
131
|
+
cron: CRON_EXPRESSIONS.DAILY_8AM,
|
|
132
|
+
description: 'Generate daily sales report',
|
|
133
|
+
taskQueue: 'reports'
|
|
97
134
|
})
|
|
98
135
|
async generateDailyReport(): Promise<void> {
|
|
99
|
-
console.log('Generating daily
|
|
100
|
-
//
|
|
136
|
+
console.log('Generating daily report...');
|
|
137
|
+
// Your report generation logic
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Cron(CRON_EXPRESSIONS.WEEKLY_MONDAY_9AM, {
|
|
141
|
+
scheduleId: 'weekly-cleanup'
|
|
142
|
+
})
|
|
143
|
+
async performWeeklyCleanup(): Promise<void> {
|
|
144
|
+
console.log('Performing weekly cleanup...');
|
|
145
|
+
// Your cleanup logic
|
|
101
146
|
}
|
|
102
147
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
scheduleId: 'hourly-cleanup',
|
|
106
|
-
description: 'Hourly cleanup task'
|
|
148
|
+
@Interval('5m', {
|
|
149
|
+
scheduleId: 'health-check'
|
|
107
150
|
})
|
|
108
|
-
async
|
|
109
|
-
console.log('
|
|
110
|
-
//
|
|
151
|
+
async performHealthCheck(): Promise<void> {
|
|
152
|
+
console.log('Performing health check...');
|
|
153
|
+
// Your health check logic
|
|
111
154
|
}
|
|
112
155
|
}
|
|
113
156
|
```
|
|
114
157
|
|
|
115
|
-
###
|
|
158
|
+
### 5. Use in Services
|
|
116
159
|
|
|
117
160
|
```typescript
|
|
118
|
-
// services/
|
|
161
|
+
// services/order.service.ts
|
|
119
162
|
import { Injectable } from '@nestjs/common';
|
|
120
163
|
import { TemporalService } from 'nestjs-temporal-core';
|
|
121
164
|
|
|
122
165
|
@Injectable()
|
|
123
|
-
export class
|
|
166
|
+
export class OrderService {
|
|
124
167
|
constructor(private readonly temporal: TemporalService) {}
|
|
125
168
|
|
|
126
|
-
async
|
|
127
|
-
// Start workflow directly with client
|
|
169
|
+
async createOrder(orderData: any) {
|
|
128
170
|
const { workflowId } = await this.temporal.startWorkflow(
|
|
129
|
-
'
|
|
130
|
-
[
|
|
131
|
-
{
|
|
132
|
-
taskQueue: '
|
|
133
|
-
workflowId: `
|
|
171
|
+
'processOrder',
|
|
172
|
+
[orderData],
|
|
173
|
+
{
|
|
174
|
+
taskQueue: 'orders',
|
|
175
|
+
workflowId: `order-${orderData.id}`,
|
|
176
|
+
searchAttributes: {
|
|
177
|
+
'customer-id': orderData.customerId
|
|
178
|
+
}
|
|
134
179
|
}
|
|
135
180
|
);
|
|
136
181
|
|
|
137
|
-
return workflowId;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async getUserStatus(workflowId: string): Promise<string> {
|
|
141
|
-
return await this.temporal.queryWorkflow(workflowId, 'getStatus');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async updateUserStatus(workflowId: string, status: string): Promise<void> {
|
|
145
|
-
await this.temporal.signalWorkflow(workflowId, 'updateStatus', [status]);
|
|
182
|
+
return { workflowId };
|
|
146
183
|
}
|
|
147
184
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
185
|
+
async cancelOrder(orderId: string) {
|
|
186
|
+
await this.temporal.signalWorkflow(`order-${orderId}`, 'cancel');
|
|
187
|
+
return { cancelled: true };
|
|
151
188
|
}
|
|
152
189
|
|
|
153
|
-
async
|
|
154
|
-
await this.temporal.
|
|
190
|
+
async getOrderStatus(orderId: string) {
|
|
191
|
+
const status = await this.temporal.queryWorkflow(`order-${orderId}`, 'getStatus');
|
|
192
|
+
return status;
|
|
155
193
|
}
|
|
156
194
|
}
|
|
157
195
|
```
|
|
158
196
|
|
|
159
|
-
##
|
|
197
|
+
## 🏗️ Integration Patterns
|
|
160
198
|
|
|
161
|
-
###
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
TemporalModule.register({
|
|
165
|
-
connection: {
|
|
166
|
-
address: 'localhost:7233',
|
|
167
|
-
namespace: 'default',
|
|
168
|
-
},
|
|
169
|
-
taskQueue: 'my-app',
|
|
170
|
-
worker: {
|
|
171
|
-
workflowsPath: './dist/workflows',
|
|
172
|
-
activityClasses: [EmailActivities, PaymentActivities],
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
```
|
|
199
|
+
### Client-Only Integration
|
|
176
200
|
|
|
177
|
-
|
|
201
|
+
For applications that only start workflows (e.g., web APIs):
|
|
178
202
|
|
|
179
203
|
```typescript
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
204
|
+
import { TemporalClientModule } from 'nestjs-temporal-core';
|
|
205
|
+
|
|
206
|
+
@Module({
|
|
207
|
+
imports: [
|
|
208
|
+
TemporalClientModule.forRoot({
|
|
209
|
+
connection: {
|
|
210
|
+
address: 'localhost:7233',
|
|
211
|
+
namespace: 'production'
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
],
|
|
215
|
+
providers: [ApiService],
|
|
216
|
+
})
|
|
217
|
+
export class ClientOnlyModule {}
|
|
187
218
|
```
|
|
188
219
|
|
|
189
|
-
### Worker-Only
|
|
220
|
+
### Worker-Only Integration
|
|
221
|
+
|
|
222
|
+
For dedicated worker processes:
|
|
190
223
|
|
|
191
224
|
```typescript
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
225
|
+
import { TemporalWorkerModule, WORKER_PRESETS } from 'nestjs-temporal-core';
|
|
226
|
+
|
|
227
|
+
@Module({
|
|
228
|
+
imports: [
|
|
229
|
+
TemporalWorkerModule.forRoot({
|
|
230
|
+
connection: {
|
|
231
|
+
address: 'localhost:7233',
|
|
232
|
+
namespace: 'production'
|
|
233
|
+
},
|
|
234
|
+
taskQueue: 'worker-queue',
|
|
235
|
+
workflowsPath: './dist/workflows',
|
|
236
|
+
activityClasses: [ProcessingActivities],
|
|
237
|
+
workerOptions: WORKER_PRESETS.PRODUCTION_HIGH_THROUGHPUT
|
|
238
|
+
})
|
|
239
|
+
],
|
|
240
|
+
providers: [ProcessingActivities],
|
|
241
|
+
})
|
|
242
|
+
export class WorkerOnlyModule {}
|
|
201
243
|
```
|
|
202
244
|
|
|
203
|
-
###
|
|
245
|
+
### Modular Integration
|
|
246
|
+
|
|
247
|
+
Using individual modules for specific needs:
|
|
204
248
|
|
|
205
249
|
```typescript
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
250
|
+
import {
|
|
251
|
+
TemporalClientModule,
|
|
252
|
+
TemporalActivityModule,
|
|
253
|
+
TemporalSchedulesModule
|
|
254
|
+
} from 'nestjs-temporal-core';
|
|
255
|
+
|
|
256
|
+
@Module({
|
|
257
|
+
imports: [
|
|
258
|
+
// Client for workflow operations
|
|
259
|
+
TemporalClientModule.forRoot({
|
|
260
|
+
connection: { address: 'localhost:7233' }
|
|
261
|
+
}),
|
|
262
|
+
|
|
263
|
+
// Activities management
|
|
264
|
+
TemporalActivityModule.forRoot({
|
|
265
|
+
activityClasses: [EmailActivities, PaymentActivities]
|
|
266
|
+
}),
|
|
267
|
+
|
|
268
|
+
// Schedule management
|
|
269
|
+
TemporalSchedulesModule.forRoot({
|
|
270
|
+
autoStart: true,
|
|
271
|
+
defaultTimezone: 'UTC'
|
|
272
|
+
}),
|
|
273
|
+
],
|
|
274
|
+
providers: [EmailActivities, PaymentActivities, ScheduledService],
|
|
275
|
+
})
|
|
276
|
+
export class ModularIntegrationModule {}
|
|
221
277
|
```
|
|
222
278
|
|
|
223
|
-
##
|
|
279
|
+
## ⚙️ Configuration
|
|
224
280
|
|
|
225
|
-
###
|
|
226
|
-
The module automatically discovers and registers:
|
|
227
|
-
- **Activity Classes** marked with `@Activity`
|
|
228
|
-
- **Scheduled Workflows** marked with `@Cron` or `@Interval`
|
|
229
|
-
- **Signals and Queries** within classes
|
|
281
|
+
### Async Configuration
|
|
230
282
|
|
|
231
|
-
### Scheduling Made Simple
|
|
232
283
|
```typescript
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
284
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
285
|
+
|
|
286
|
+
@Module({
|
|
287
|
+
imports: [
|
|
288
|
+
ConfigModule.forRoot(),
|
|
289
|
+
TemporalModule.registerAsync({
|
|
290
|
+
imports: [ConfigModule],
|
|
291
|
+
useFactory: async (config: ConfigService) => ({
|
|
292
|
+
connection: {
|
|
293
|
+
address: config.get('TEMPORAL_ADDRESS'),
|
|
294
|
+
namespace: config.get('TEMPORAL_NAMESPACE'),
|
|
295
|
+
tls: config.get('TEMPORAL_TLS_ENABLED') === 'true',
|
|
296
|
+
apiKey: config.get('TEMPORAL_API_KEY'),
|
|
297
|
+
},
|
|
298
|
+
taskQueue: config.get('TEMPORAL_TASK_QUEUE'),
|
|
299
|
+
worker: {
|
|
300
|
+
workflowsPath: config.get('WORKFLOWS_PATH'),
|
|
301
|
+
activityClasses: [EmailActivities, PaymentActivities],
|
|
302
|
+
autoStart: config.get('WORKER_AUTO_START') !== 'false',
|
|
303
|
+
}
|
|
304
|
+
}),
|
|
305
|
+
inject: [ConfigService],
|
|
306
|
+
})
|
|
307
|
+
],
|
|
308
|
+
})
|
|
309
|
+
export class AppModule {}
|
|
238
310
|
```
|
|
239
311
|
|
|
240
|
-
|
|
312
|
+
### Environment-Specific Configurations
|
|
241
313
|
|
|
242
|
-
### Scheduled Reports
|
|
243
314
|
```typescript
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
315
|
+
// Development
|
|
316
|
+
const developmentConfig = {
|
|
317
|
+
connection: {
|
|
318
|
+
address: 'localhost:7233',
|
|
319
|
+
namespace: 'development'
|
|
320
|
+
},
|
|
321
|
+
taskQueue: 'dev-queue',
|
|
322
|
+
worker: {
|
|
323
|
+
workflowsPath: './dist/workflows',
|
|
324
|
+
workerOptions: WORKER_PRESETS.DEVELOPMENT
|
|
250
325
|
}
|
|
251
|
-
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// Production
|
|
329
|
+
const productionConfig = {
|
|
330
|
+
connection: {
|
|
331
|
+
address: process.env.TEMPORAL_ADDRESS!,
|
|
332
|
+
namespace: process.env.TEMPORAL_NAMESPACE!,
|
|
333
|
+
tls: true,
|
|
334
|
+
apiKey: process.env.TEMPORAL_API_KEY
|
|
335
|
+
},
|
|
336
|
+
taskQueue: process.env.TEMPORAL_TASK_QUEUE!,
|
|
337
|
+
worker: {
|
|
338
|
+
workflowBundle: require('../workflows/bundle'), // Pre-bundled
|
|
339
|
+
workerOptions: WORKER_PRESETS.PRODUCTION_BALANCED
|
|
340
|
+
}
|
|
341
|
+
};
|
|
252
342
|
```
|
|
253
343
|
|
|
254
|
-
|
|
344
|
+
## 📊 Health Monitoring
|
|
345
|
+
|
|
346
|
+
Built-in health monitoring for production environments:
|
|
347
|
+
|
|
255
348
|
```typescript
|
|
256
|
-
@
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return
|
|
349
|
+
@Controller('health')
|
|
350
|
+
export class HealthController {
|
|
351
|
+
constructor(private readonly temporal: TemporalService) {}
|
|
352
|
+
|
|
353
|
+
@Get('temporal')
|
|
354
|
+
async getTemporalHealth() {
|
|
355
|
+
const health = await this.temporal.getOverallHealth();
|
|
356
|
+
return {
|
|
357
|
+
status: health.status,
|
|
358
|
+
components: health.components,
|
|
359
|
+
timestamp: new Date().toISOString()
|
|
360
|
+
};
|
|
264
361
|
}
|
|
265
362
|
|
|
266
|
-
@
|
|
267
|
-
async
|
|
268
|
-
|
|
269
|
-
|
|
363
|
+
@Get('temporal/detailed')
|
|
364
|
+
async getDetailedStatus() {
|
|
365
|
+
const systemStatus = await this.temporal.getSystemStatus();
|
|
366
|
+
const stats = this.temporal.getDiscoveryStats();
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
system: systemStatus,
|
|
370
|
+
discovery: stats,
|
|
371
|
+
schedules: this.temporal.getScheduleStats()
|
|
372
|
+
};
|
|
270
373
|
}
|
|
271
374
|
}
|
|
272
375
|
```
|
|
273
376
|
|
|
274
|
-
|
|
377
|
+
## 🔧 Advanced Features
|
|
378
|
+
|
|
379
|
+
### Activity Options
|
|
380
|
+
|
|
275
381
|
```typescript
|
|
382
|
+
@Activity()
|
|
276
383
|
@Injectable()
|
|
277
|
-
export class
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
384
|
+
export class PaymentActivities {
|
|
385
|
+
|
|
386
|
+
@ActivityMethod({
|
|
387
|
+
name: 'processPayment',
|
|
388
|
+
timeout: '2m',
|
|
389
|
+
maxRetries: 5,
|
|
390
|
+
retryPolicy: {
|
|
391
|
+
maximumAttempts: 5,
|
|
392
|
+
initialInterval: '1s',
|
|
393
|
+
maximumInterval: '60s',
|
|
394
|
+
backoffCoefficient: 2.0
|
|
395
|
+
}
|
|
281
396
|
})
|
|
282
|
-
async
|
|
283
|
-
|
|
284
|
-
// Health check logic
|
|
397
|
+
async processPayment(orderId: string, amount: number) {
|
|
398
|
+
// Complex payment processing with retries
|
|
285
399
|
}
|
|
286
400
|
}
|
|
287
401
|
```
|
|
288
402
|
|
|
289
|
-
|
|
403
|
+
### Schedule Management
|
|
290
404
|
|
|
291
405
|
```typescript
|
|
292
406
|
@Injectable()
|
|
293
|
-
export class
|
|
407
|
+
export class ScheduleManagementService {
|
|
294
408
|
constructor(private readonly temporal: TemporalService) {}
|
|
295
409
|
|
|
296
|
-
async
|
|
297
|
-
|
|
298
|
-
const health = await this.temporal.getOverallHealth();
|
|
299
|
-
return {
|
|
300
|
-
status: health.status, // 'healthy' | 'degraded' | 'unhealthy'
|
|
301
|
-
components: health.components,
|
|
302
|
-
};
|
|
410
|
+
async pauseSchedule(scheduleId: string) {
|
|
411
|
+
await this.temporal.pauseSchedule(scheduleId, 'Maintenance mode');
|
|
303
412
|
}
|
|
304
413
|
|
|
305
|
-
async
|
|
306
|
-
|
|
307
|
-
const schedules = this.temporal.getManagedSchedules();
|
|
308
|
-
const stats = this.temporal.getDiscoveryStats();
|
|
309
|
-
|
|
310
|
-
return { schedules, stats };
|
|
414
|
+
async resumeSchedule(scheduleId: string) {
|
|
415
|
+
await this.temporal.resumeSchedule(scheduleId);
|
|
311
416
|
}
|
|
312
417
|
|
|
313
|
-
async
|
|
314
|
-
|
|
315
|
-
await this.temporal.pauseSchedule('daily-report', 'Maintenance');
|
|
316
|
-
await this.temporal.resumeSchedule('daily-report');
|
|
317
|
-
await this.temporal.triggerSchedule('daily-report'); // Run now
|
|
418
|
+
async triggerScheduleNow(scheduleId: string) {
|
|
419
|
+
await this.temporal.triggerSchedule(scheduleId);
|
|
318
420
|
}
|
|
421
|
+
|
|
422
|
+
async getScheduleInfo(scheduleId: string) {
|
|
423
|
+
return this.temporal.getScheduleInfo(scheduleId);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Workflow Signals and Queries
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
// In your workflow
|
|
432
|
+
import { defineSignal, defineQuery, setHandler } from '@temporalio/workflow';
|
|
433
|
+
|
|
434
|
+
export const cancelSignal = defineSignal('cancel');
|
|
435
|
+
export const getStatusQuery = defineQuery<string>('getStatus');
|
|
436
|
+
|
|
437
|
+
export async function orderWorkflow(orderData: any) {
|
|
438
|
+
let status = 'processing';
|
|
439
|
+
let cancelled = false;
|
|
440
|
+
|
|
441
|
+
// Handle cancel signal
|
|
442
|
+
setHandler(cancelSignal, () => {
|
|
443
|
+
cancelled = true;
|
|
444
|
+
status = 'cancelled';
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Handle status query
|
|
448
|
+
setHandler(getStatusQuery, () => status);
|
|
449
|
+
|
|
450
|
+
// Workflow logic with cancellation support
|
|
451
|
+
if (cancelled) return;
|
|
452
|
+
|
|
453
|
+
// Process order...
|
|
454
|
+
status = 'completed';
|
|
319
455
|
}
|
|
320
456
|
```
|
|
321
457
|
|
|
322
|
-
##
|
|
458
|
+
## 🌐 Temporal Cloud Integration
|
|
323
459
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
460
|
+
For Temporal Cloud deployments:
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
TemporalModule.register({
|
|
464
|
+
connection: {
|
|
465
|
+
address: 'your-namespace.account.tmprl.cloud:7233',
|
|
466
|
+
namespace: 'your-namespace.account',
|
|
467
|
+
tls: true,
|
|
468
|
+
apiKey: process.env.TEMPORAL_API_KEY,
|
|
469
|
+
metadata: {
|
|
470
|
+
'temporal-namespace': 'your-namespace.account'
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
taskQueue: 'production-queue',
|
|
474
|
+
worker: {
|
|
475
|
+
workflowBundle: require('../workflows/bundle'),
|
|
476
|
+
workerOptions: WORKER_PRESETS.PRODUCTION_BALANCED
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## 📋 Best Practices
|
|
482
|
+
|
|
483
|
+
### 1. **Activity Design**
|
|
484
|
+
- Keep activities idempotent
|
|
485
|
+
- Use proper timeouts and retry policies
|
|
486
|
+
- Handle errors gracefully
|
|
487
|
+
- Use dependency injection for testability
|
|
488
|
+
|
|
489
|
+
### 2. **Workflow Organization**
|
|
490
|
+
- Separate workflow files from activities
|
|
491
|
+
- Use TypeScript for type safety
|
|
492
|
+
- Keep workflows deterministic
|
|
493
|
+
- Bundle workflows for production
|
|
494
|
+
|
|
495
|
+
### 3. **Configuration Management**
|
|
496
|
+
- Use environment variables for connection settings
|
|
497
|
+
- Separate configs for different environments
|
|
498
|
+
- Use async configuration for dynamic settings
|
|
499
|
+
- Validate configuration at startup
|
|
500
|
+
|
|
501
|
+
### 4. **Monitoring & Observability**
|
|
502
|
+
- Implement health checks
|
|
503
|
+
- Monitor worker status
|
|
504
|
+
- Track schedule execution
|
|
505
|
+
- Use structured logging
|
|
506
|
+
|
|
507
|
+
### 5. **Production Deployment**
|
|
508
|
+
- Use pre-bundled workflows
|
|
509
|
+
- Configure appropriate worker limits
|
|
510
|
+
- Enable TLS for security
|
|
511
|
+
- Implement graceful shutdowns
|
|
512
|
+
|
|
513
|
+
## 📚 API Reference
|
|
514
|
+
|
|
515
|
+
### Decorators
|
|
516
|
+
|
|
517
|
+
- `@Activity()` - Mark a class as containing activities
|
|
518
|
+
- `@ActivityMethod(options?)` - Define an activity method
|
|
519
|
+
- `@Scheduled(options)` - Schedule a workflow with full options
|
|
520
|
+
- `@Cron(expression, options?)` - Schedule using cron expression
|
|
521
|
+
- `@Interval(interval, options?)` - Schedule using interval
|
|
522
|
+
|
|
523
|
+
### Services
|
|
524
|
+
|
|
525
|
+
- `TemporalService` - Main service for all operations
|
|
526
|
+
- `TemporalClientService` - Client-only operations
|
|
527
|
+
- `TemporalActivityService` - Activity management
|
|
528
|
+
- `TemporalSchedulesService` - Schedule management
|
|
529
|
+
- `TemporalWorkerManagerService` - Worker lifecycle
|
|
530
|
+
|
|
531
|
+
### Constants
|
|
532
|
+
|
|
533
|
+
- `CRON_EXPRESSIONS` - Common cron patterns
|
|
534
|
+
- `INTERVAL_EXPRESSIONS` - Common interval patterns
|
|
535
|
+
- `WORKER_PRESETS` - Environment-specific worker configs
|
|
536
|
+
- `RETRY_POLICIES` - Common retry patterns
|
|
537
|
+
- `TIMEOUTS` - Common timeout values
|
|
330
538
|
|
|
331
539
|
## 🤝 Contributing
|
|
332
540
|
|
|
333
|
-
Contributions welcome! Please read our [Contributing Guide](
|
|
541
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
|
334
542
|
|
|
335
543
|
## 📄 License
|
|
336
544
|
|
|
337
|
-
MIT License - see [LICENSE](LICENSE) file for details.
|
|
545
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
546
|
+
|
|
547
|
+
## 🙏 Acknowledgments
|
|
548
|
+
|
|
549
|
+
- [Temporal.io](https://temporal.io/) for the amazing workflow engine
|
|
550
|
+
- [NestJS](https://nestjs.com/) for the fantastic framework
|
|
551
|
+
- The TypeScript community for excellent tooling
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
Built with ❤️ for the NestJS and Temporal communities
|