nestjs-temporal-core 3.0.4 → 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 +391 -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
|
@@ -23,7 +23,8 @@ NestJS Temporal Core brings Temporal's durable execution to NestJS with familiar
|
|
|
23
23
|
- **⚙️ Flexible Setup** - Client-only, worker-only, or unified deployments
|
|
24
24
|
- **🏥 Health Monitoring** - Comprehensive status monitoring and health checks
|
|
25
25
|
- **🔧 Production Ready** - TLS, connection management, graceful shutdowns
|
|
26
|
-
- **📊
|
|
26
|
+
- **📊 Modular Architecture** - Individual modules for specific needs
|
|
27
|
+
- **🔐 Enterprise Ready** - Temporal Cloud support with TLS and API keys
|
|
27
28
|
|
|
28
29
|
## 📦 Installation
|
|
29
30
|
|
|
@@ -33,7 +34,9 @@ npm install nestjs-temporal-core @temporalio/client @temporalio/worker @temporal
|
|
|
33
34
|
|
|
34
35
|
## 🚀 Quick Start
|
|
35
36
|
|
|
36
|
-
### 1.
|
|
37
|
+
### 1. Complete Integration (Recommended)
|
|
38
|
+
|
|
39
|
+
For applications that need full Temporal functionality:
|
|
37
40
|
|
|
38
41
|
```typescript
|
|
39
42
|
// app.module.ts
|
|
@@ -48,294 +51,505 @@ import { EmailActivities } from './activities/email.activities';
|
|
|
48
51
|
address: 'localhost:7233',
|
|
49
52
|
namespace: 'default',
|
|
50
53
|
},
|
|
51
|
-
taskQueue: '
|
|
54
|
+
taskQueue: 'main-queue',
|
|
52
55
|
worker: {
|
|
53
56
|
workflowsPath: './dist/workflows',
|
|
54
|
-
activityClasses: [EmailActivities],
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
activityClasses: [EmailActivities],
|
|
58
|
+
autoStart: true
|
|
59
|
+
}
|
|
60
|
+
})
|
|
57
61
|
],
|
|
58
|
-
providers: [EmailActivities],
|
|
62
|
+
providers: [EmailActivities], // Auto-discovered
|
|
59
63
|
})
|
|
60
64
|
export class AppModule {}
|
|
61
65
|
```
|
|
62
66
|
|
|
63
|
-
### 2.
|
|
67
|
+
### 2. Define Activities
|
|
64
68
|
|
|
65
69
|
```typescript
|
|
66
70
|
// activities/email.activities.ts
|
|
67
71
|
import { Injectable } from '@nestjs/common';
|
|
68
72
|
import { Activity, ActivityMethod } from 'nestjs-temporal-core';
|
|
69
73
|
|
|
70
|
-
@Injectable()
|
|
71
74
|
@Activity()
|
|
75
|
+
@Injectable()
|
|
72
76
|
export class EmailActivities {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
@ActivityMethod()
|
|
81
|
-
async sendNotification(
|
|
82
|
-
console.log(`
|
|
88
|
+
@ActivityMethod('sendNotification')
|
|
89
|
+
async sendNotification(userId: string, message: string): Promise<void> {
|
|
90
|
+
console.log(`Notifying user ${userId}: ${message}`);
|
|
83
91
|
// Your notification logic here
|
|
84
92
|
}
|
|
85
93
|
}
|
|
86
94
|
```
|
|
87
95
|
|
|
88
|
-
### 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
|
|
89
120
|
|
|
90
121
|
```typescript
|
|
91
122
|
// services/scheduled.service.ts
|
|
92
123
|
import { Injectable } from '@nestjs/common';
|
|
93
|
-
import { Cron, Interval } from 'nestjs-temporal-core';
|
|
124
|
+
import { Scheduled, Cron, Interval, CRON_EXPRESSIONS } from 'nestjs-temporal-core';
|
|
94
125
|
|
|
95
126
|
@Injectable()
|
|
96
127
|
export class ScheduledService {
|
|
97
|
-
|
|
98
|
-
@
|
|
99
|
-
scheduleId: 'daily-
|
|
100
|
-
|
|
128
|
+
|
|
129
|
+
@Scheduled({
|
|
130
|
+
scheduleId: 'daily-report',
|
|
131
|
+
cron: CRON_EXPRESSIONS.DAILY_8AM,
|
|
132
|
+
description: 'Generate daily sales report',
|
|
133
|
+
taskQueue: 'reports'
|
|
101
134
|
})
|
|
102
135
|
async generateDailyReport(): Promise<void> {
|
|
103
|
-
console.log('Generating daily
|
|
104
|
-
//
|
|
136
|
+
console.log('Generating daily report...');
|
|
137
|
+
// Your report generation logic
|
|
105
138
|
}
|
|
106
139
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
scheduleId: 'hourly-cleanup',
|
|
110
|
-
description: 'Hourly cleanup task'
|
|
140
|
+
@Cron(CRON_EXPRESSIONS.WEEKLY_MONDAY_9AM, {
|
|
141
|
+
scheduleId: 'weekly-cleanup'
|
|
111
142
|
})
|
|
112
|
-
async
|
|
113
|
-
console.log('
|
|
114
|
-
//
|
|
143
|
+
async performWeeklyCleanup(): Promise<void> {
|
|
144
|
+
console.log('Performing weekly cleanup...');
|
|
145
|
+
// Your cleanup logic
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@Interval('5m', {
|
|
149
|
+
scheduleId: 'health-check'
|
|
150
|
+
})
|
|
151
|
+
async performHealthCheck(): Promise<void> {
|
|
152
|
+
console.log('Performing health check...');
|
|
153
|
+
// Your health check logic
|
|
115
154
|
}
|
|
116
155
|
}
|
|
117
156
|
```
|
|
118
157
|
|
|
119
|
-
###
|
|
158
|
+
### 5. Use in Services
|
|
120
159
|
|
|
121
160
|
```typescript
|
|
122
|
-
// services/
|
|
161
|
+
// services/order.service.ts
|
|
123
162
|
import { Injectable } from '@nestjs/common';
|
|
124
163
|
import { TemporalService } from 'nestjs-temporal-core';
|
|
125
164
|
|
|
126
165
|
@Injectable()
|
|
127
|
-
export class
|
|
166
|
+
export class OrderService {
|
|
128
167
|
constructor(private readonly temporal: TemporalService) {}
|
|
129
168
|
|
|
130
|
-
async
|
|
131
|
-
// Start workflow directly with client
|
|
169
|
+
async createOrder(orderData: any) {
|
|
132
170
|
const { workflowId } = await this.temporal.startWorkflow(
|
|
133
|
-
'
|
|
134
|
-
[
|
|
135
|
-
{
|
|
136
|
-
taskQueue: '
|
|
137
|
-
workflowId: `
|
|
171
|
+
'processOrder',
|
|
172
|
+
[orderData],
|
|
173
|
+
{
|
|
174
|
+
taskQueue: 'orders',
|
|
175
|
+
workflowId: `order-${orderData.id}`,
|
|
176
|
+
searchAttributes: {
|
|
177
|
+
'customer-id': orderData.customerId
|
|
178
|
+
}
|
|
138
179
|
}
|
|
139
180
|
);
|
|
140
181
|
|
|
141
|
-
return workflowId;
|
|
182
|
+
return { workflowId };
|
|
142
183
|
}
|
|
143
184
|
|
|
144
|
-
async
|
|
145
|
-
|
|
185
|
+
async cancelOrder(orderId: string) {
|
|
186
|
+
await this.temporal.signalWorkflow(`order-${orderId}`, 'cancel');
|
|
187
|
+
return { cancelled: true };
|
|
146
188
|
}
|
|
147
189
|
|
|
148
|
-
async
|
|
149
|
-
await this.temporal.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// Schedule management
|
|
153
|
-
async pauseDailyReport(): Promise<void> {
|
|
154
|
-
await this.temporal.pauseSchedule('daily-user-report', 'Maintenance mode');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async resumeDailyReport(): Promise<void> {
|
|
158
|
-
await this.temporal.resumeSchedule('daily-user-report');
|
|
190
|
+
async getOrderStatus(orderId: string) {
|
|
191
|
+
const status = await this.temporal.queryWorkflow(`order-${orderId}`, 'getStatus');
|
|
192
|
+
return status;
|
|
159
193
|
}
|
|
160
194
|
}
|
|
161
195
|
```
|
|
162
196
|
|
|
163
|
-
##
|
|
197
|
+
## 🏗️ Integration Patterns
|
|
164
198
|
|
|
165
|
-
###
|
|
199
|
+
### Client-Only Integration
|
|
166
200
|
|
|
167
|
-
|
|
168
|
-
TemporalModule.register({
|
|
169
|
-
connection: {
|
|
170
|
-
address: 'localhost:7233',
|
|
171
|
-
namespace: 'default',
|
|
172
|
-
},
|
|
173
|
-
taskQueue: 'my-app',
|
|
174
|
-
worker: {
|
|
175
|
-
workflowsPath: './dist/workflows',
|
|
176
|
-
activityClasses: [EmailActivities, PaymentActivities],
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Client-Only Mode
|
|
201
|
+
For applications that only start workflows (e.g., web APIs):
|
|
182
202
|
|
|
183
203
|
```typescript
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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 {}
|
|
191
218
|
```
|
|
192
219
|
|
|
193
|
-
### Worker-Only
|
|
220
|
+
### Worker-Only Integration
|
|
221
|
+
|
|
222
|
+
For dedicated worker processes:
|
|
194
223
|
|
|
195
224
|
```typescript
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
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 {}
|
|
205
243
|
```
|
|
206
244
|
|
|
207
|
-
###
|
|
245
|
+
### Modular Integration
|
|
246
|
+
|
|
247
|
+
Using individual modules for specific needs:
|
|
208
248
|
|
|
209
249
|
```typescript
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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 {}
|
|
225
277
|
```
|
|
226
278
|
|
|
227
|
-
##
|
|
279
|
+
## ⚙️ Configuration
|
|
228
280
|
|
|
229
|
-
###
|
|
230
|
-
The module automatically discovers and registers:
|
|
231
|
-
- **Activity Classes** marked with `@Activity`
|
|
232
|
-
- **Scheduled Workflows** marked with `@Cron` or `@Interval`
|
|
233
|
-
- **Signals and Queries** within classes
|
|
281
|
+
### Async Configuration
|
|
234
282
|
|
|
235
|
-
### Scheduling Made Simple
|
|
236
283
|
```typescript
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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 {}
|
|
242
310
|
```
|
|
243
311
|
|
|
244
|
-
|
|
312
|
+
### Environment-Specific Configurations
|
|
245
313
|
|
|
246
|
-
### Scheduled Reports
|
|
247
314
|
```typescript
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
254
325
|
}
|
|
255
|
-
}
|
|
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
|
+
};
|
|
256
342
|
```
|
|
257
343
|
|
|
258
|
-
|
|
344
|
+
## 📊 Health Monitoring
|
|
345
|
+
|
|
346
|
+
Built-in health monitoring for production environments:
|
|
347
|
+
|
|
259
348
|
```typescript
|
|
260
|
-
@
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
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
|
+
};
|
|
268
361
|
}
|
|
269
362
|
|
|
270
|
-
@
|
|
271
|
-
async
|
|
272
|
-
|
|
273
|
-
|
|
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
|
+
};
|
|
274
373
|
}
|
|
275
374
|
}
|
|
276
375
|
```
|
|
277
376
|
|
|
278
|
-
|
|
377
|
+
## 🔧 Advanced Features
|
|
378
|
+
|
|
379
|
+
### Activity Options
|
|
380
|
+
|
|
279
381
|
```typescript
|
|
382
|
+
@Activity()
|
|
280
383
|
@Injectable()
|
|
281
|
-
export class
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
+
}
|
|
285
396
|
})
|
|
286
|
-
async
|
|
287
|
-
|
|
288
|
-
// Health check logic
|
|
397
|
+
async processPayment(orderId: string, amount: number) {
|
|
398
|
+
// Complex payment processing with retries
|
|
289
399
|
}
|
|
290
400
|
}
|
|
291
401
|
```
|
|
292
402
|
|
|
293
|
-
|
|
403
|
+
### Schedule Management
|
|
294
404
|
|
|
295
405
|
```typescript
|
|
296
406
|
@Injectable()
|
|
297
|
-
export class
|
|
407
|
+
export class ScheduleManagementService {
|
|
298
408
|
constructor(private readonly temporal: TemporalService) {}
|
|
299
409
|
|
|
300
|
-
async
|
|
301
|
-
|
|
302
|
-
const health = await this.temporal.getOverallHealth();
|
|
303
|
-
return {
|
|
304
|
-
status: health.status, // 'healthy' | 'degraded' | 'unhealthy'
|
|
305
|
-
components: health.components,
|
|
306
|
-
};
|
|
410
|
+
async pauseSchedule(scheduleId: string) {
|
|
411
|
+
await this.temporal.pauseSchedule(scheduleId, 'Maintenance mode');
|
|
307
412
|
}
|
|
308
413
|
|
|
309
|
-
async
|
|
310
|
-
|
|
311
|
-
const schedules = this.temporal.getManagedSchedules();
|
|
312
|
-
const stats = this.temporal.getDiscoveryStats();
|
|
313
|
-
|
|
314
|
-
return { schedules, stats };
|
|
414
|
+
async resumeSchedule(scheduleId: string) {
|
|
415
|
+
await this.temporal.resumeSchedule(scheduleId);
|
|
315
416
|
}
|
|
316
417
|
|
|
317
|
-
async
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
418
|
+
async triggerScheduleNow(scheduleId: string) {
|
|
419
|
+
await this.temporal.triggerSchedule(scheduleId);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async getScheduleInfo(scheduleId: string) {
|
|
423
|
+
return this.temporal.getScheduleInfo(scheduleId);
|
|
322
424
|
}
|
|
323
425
|
}
|
|
324
426
|
```
|
|
325
427
|
|
|
326
|
-
|
|
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';
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## 🌐 Temporal Cloud Integration
|
|
459
|
+
|
|
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
|
|
327
514
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
-
|
|
331
|
-
-
|
|
332
|
-
-
|
|
333
|
-
-
|
|
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
|
|
334
538
|
|
|
335
539
|
## 🤝 Contributing
|
|
336
540
|
|
|
337
|
-
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.
|
|
338
542
|
|
|
339
543
|
## 📄 License
|
|
340
544
|
|
|
341
|
-
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
|