nestjs-temporal-core 2.0.6 → 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.
Files changed (73) hide show
  1. package/README.md +434 -325
  2. package/dist/client/temporal-client.module.js +1 -4
  3. package/dist/client/temporal-client.module.js.map +1 -1
  4. package/dist/client/temporal-client.service.d.ts +0 -2
  5. package/dist/client/temporal-client.service.js +1 -35
  6. package/dist/client/temporal-client.service.js.map +1 -1
  7. package/dist/client/temporal-schedule.service.d.ts +1 -11
  8. package/dist/client/temporal-schedule.service.js +12 -108
  9. package/dist/client/temporal-schedule.service.js.map +1 -1
  10. package/dist/constants.d.ts +0 -39
  11. package/dist/constants.js +1 -36
  12. package/dist/constants.js.map +1 -1
  13. package/dist/decorators/activity-method.decorator.js.map +1 -1
  14. package/dist/decorators/index.d.ts +2 -4
  15. package/dist/decorators/index.js +2 -4
  16. package/dist/decorators/index.js.map +1 -1
  17. package/dist/decorators/query-method.decorator.d.ts +1 -0
  18. package/dist/decorators/{query.decorator.js → query-method.decorator.js} +5 -14
  19. package/dist/decorators/query-method.decorator.js.map +1 -0
  20. package/dist/decorators/signal-method.decorator.d.ts +1 -0
  21. package/dist/decorators/{signal.decorator.js → signal-method.decorator.js} +5 -14
  22. package/dist/decorators/signal-method.decorator.js.map +1 -0
  23. package/dist/decorators/workflow-method.decorator.d.ts +1 -1
  24. package/dist/decorators/workflow-method.decorator.js +2 -5
  25. package/dist/decorators/workflow-method.decorator.js.map +1 -1
  26. package/dist/decorators/workflow.decorator.d.ts +2 -2
  27. package/dist/decorators/workflow.decorator.js +8 -13
  28. package/dist/decorators/workflow.decorator.js.map +1 -1
  29. package/dist/index.d.ts +5 -5
  30. package/dist/index.js +8 -34
  31. package/dist/index.js.map +1 -1
  32. package/dist/interfaces/activity.interface.d.ts +2 -15
  33. package/dist/interfaces/base.interface.d.ts +8 -19
  34. package/dist/interfaces/client.interface.d.ts +6 -33
  35. package/dist/interfaces/client.interface.js +0 -11
  36. package/dist/interfaces/client.interface.js.map +1 -1
  37. package/dist/interfaces/index.d.ts +4 -6
  38. package/dist/interfaces/index.js +2 -10
  39. package/dist/interfaces/index.js.map +1 -1
  40. package/dist/interfaces/temporal.interface.d.ts +33 -0
  41. package/dist/interfaces/{schedule.interface.js → temporal.interface.js} +1 -1
  42. package/dist/interfaces/temporal.interface.js.map +1 -0
  43. package/dist/interfaces/worker.interface.d.ts +2 -33
  44. package/dist/interfaces/workflow.interface.d.ts +9 -20
  45. package/dist/temporal.module.d.ts +6 -0
  46. package/dist/temporal.module.js +101 -0
  47. package/dist/temporal.module.js.map +1 -0
  48. package/dist/temporal.service.d.ts +26 -0
  49. package/dist/temporal.service.js +64 -0
  50. package/dist/temporal.service.js.map +1 -0
  51. package/dist/tsconfig.tsbuildinfo +1 -1
  52. package/dist/worker/index.js.map +1 -1
  53. package/dist/worker/temporal-metadata.accessor.d.ts +0 -10
  54. package/dist/worker/temporal-metadata.accessor.js +0 -60
  55. package/dist/worker/temporal-metadata.accessor.js.map +1 -1
  56. package/dist/worker/temporal-worker.module.js +2 -4
  57. package/dist/worker/temporal-worker.module.js.map +1 -1
  58. package/dist/worker/worker-manager.service.d.ts +1 -1
  59. package/dist/worker/worker-manager.service.js +25 -58
  60. package/dist/worker/worker-manager.service.js.map +1 -1
  61. package/package.json +1 -1
  62. package/dist/decorators/query.decorator.d.ts +0 -2
  63. package/dist/decorators/query.decorator.js.map +0 -1
  64. package/dist/decorators/scheduled-workflow.decorator.d.ts +0 -13
  65. package/dist/decorators/scheduled-workflow.decorator.js +0 -31
  66. package/dist/decorators/scheduled-workflow.decorator.js.map +0 -1
  67. package/dist/decorators/signal.decorator.d.ts +0 -2
  68. package/dist/decorators/signal.decorator.js.map +0 -1
  69. package/dist/decorators/update.decorator.d.ts +0 -2
  70. package/dist/decorators/update.decorator.js +0 -34
  71. package/dist/decorators/update.decorator.js.map +0 -1
  72. package/dist/interfaces/schedule.interface.d.ts +0 -40
  73. package/dist/interfaces/schedule.interface.js.map +0 -1
package/README.md CHANGED
@@ -1,25 +1,21 @@
1
1
  # NestJS Temporal Core
2
2
 
3
- A robust NestJS integration for [Temporal.io](https://temporal.io/) that provides seamless worker and client support for building reliable distributed applications.
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 simplifies building fault-tolerant, long-running processes in your NestJS applications by integrating with Temporal.io - a durable execution system for reliable microservices and workflow orchestration.
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** - Register modules with sync/async configuration
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()`, `@Workflow()`, `@Signal()`, `@Query()`, and `@Update()` decorators
14
- - 🔌 **Connection Management** - Built-in connection handling with TLS support
15
- - 🔒 **Type Safety** - Strongly typed interfaces for all Temporal concepts
16
- - 📡 **Client Operation Utilities** - Methods for starting, signaling, querying, updating and managing workflows
17
- - 📊 **Monitoring Support** - Worker status tracking and metrics
18
- - 📅 **Scheduling** - Native support for cron and interval-based workflow 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 Modules
41
+ ### 2. Register the Module
46
42
 
47
43
  ```typescript
48
44
  import { Module } from '@nestjs/common';
49
- import { TemporalWorkerModule, TemporalClientModule } from 'nestjs-temporal-core';
45
+ import { TemporalModule } from 'nestjs-temporal-core';
46
+ import { EmailActivities } from './activities/email.activities';
50
47
 
51
48
  @Module({
52
49
  imports: [
53
- TemporalWorkerModule.register({
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
- workflowsPath: require.resolve('./workflows'),
60
- activityClasses: [MyActivity],
61
- autoStart: {
62
- enabled: true,
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
- name: 'PaymentActivities', // Optional custom name
84
- description: 'Activities for payment processing',
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
- return 'payment-id';
78
+ console.log(`Sending welcome email to ${to}`);
79
+ return true;
96
80
  }
97
81
 
98
- @ActivityMethod()
99
- async refundPayment(paymentId: string): Promise<boolean> {
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
- // Reference activities in your workflow
113
- const { processPayment, refundPayment } = proxyActivities<PaymentActivity>({
114
- startToCloseTimeout: '30 seconds',
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 paymentWorkflow(amount: number): Promise<string> {
118
- return await processPayment(amount);
109
+ export async function sendWelcomeWorkflow(email: string): Promise<boolean> {
110
+ return await activities.sendWelcomeEmail(email);
119
111
  }
120
112
 
121
- export async function refundWorkflow(paymentId: string): Promise<boolean> {
122
- return await refundPayment(paymentId);
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 Client Service
118
+ ### 5. Use the Temporal Service
127
119
 
128
120
  ```typescript
129
121
  import { Injectable } from '@nestjs/common';
130
- import { TemporalClientService } from 'nestjs-temporal-core';
122
+ import { TemporalService } from 'nestjs-temporal-core';
131
123
 
132
124
  @Injectable()
133
- export class PaymentService {
134
- constructor(private readonly temporalClient: TemporalClientService) {}
135
-
136
- async initiatePayment(amount: number) {
137
- const { result, workflowId } = await this.temporalClient.startWorkflow<string, [number]>(
138
- 'paymentWorkflow',
139
- [amount],
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
- taskQueue: 'my-task-queue',
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
- // Wait for the workflow to complete
151
- const paymentId = await result;
152
-
153
- return { paymentId, workflowId };
139
+ return workflowId;
154
140
  }
155
141
 
156
- async checkPaymentStatus(workflowId: string) {
157
- // Query a running workflow
158
- return await this.temporalClient.queryWorkflow<string>(workflowId, 'getPaymentStatus');
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
- async cancelPayment(workflowId: string, reason: string) {
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
- ### Class-Based Workflows with Queries, Signals, and Updates
161
+ ### Using Signals and Queries
171
162
 
172
163
  ```typescript
173
- import { Workflow, WorkflowMethod, Query, Signal, Update } from 'nestjs-temporal-core';
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
- ```typescript
218
- import { ScheduledWorkflow, WorkflowMethod } from 'nestjs-temporal-core';
167
+ @Injectable()
168
+ export class OrderService {
169
+ constructor(private readonly temporalService: TemporalService) {}
219
170
 
220
- @ScheduledWorkflow({
221
- taskQueue: 'scheduled-tasks',
222
- schedule: {
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
- @ScheduledWorkflow({
235
- taskQueue: 'scheduled-tasks',
236
- schedule: {
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
- ### Using the Schedule Service
183
+ ### Schedule Management
252
184
 
253
185
  ```typescript
254
186
  import { Injectable } from '@nestjs/common';
255
- import { TemporalScheduleService } from 'nestjs-temporal-core';
187
+ import { TemporalService } from 'nestjs-temporal-core';
256
188
 
257
189
  @Injectable()
258
190
  export class ReportingService {
259
- constructor(private readonly scheduleService: TemporalScheduleService) {}
191
+ constructor(private readonly temporalService: TemporalService) {}
260
192
 
261
- async createDailyReportSchedule() {
262
- return await this.scheduleService.createCronWorkflow(
263
- 'daily-sales-report',
264
- 'generateSalesReport',
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
- [], // No arguments
268
- 'Daily sales report generation',
199
+ ['daily-summary'],
269
200
  );
270
- }
271
201
 
272
- async createHourlyDataBackup() {
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 pauseReports() {
284
- await this.scheduleService.pauseSchedule(
285
- 'daily-sales-report',
286
- 'Paused for system maintenance',
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 listAllSchedules() {
291
- return await this.scheduleService.listSchedules();
211
+ async resumeReporting(): Promise<void> {
212
+ await this.temporalService.getScheduleService().resumeSchedule('daily-report');
292
213
  }
293
214
  }
294
215
  ```
295
216
 
296
- ### Using Workflow Updates
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
- TemporalWorkerModule.registerAsync({
331
- imports: [ConfigModule],
332
- useFactory: async (configService: ConfigService) => ({
333
- connection: {
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
- ### TLS Configuration
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
- Set up secure communications with TLS:
253
+ ### Secure Connection with TLS
354
254
 
355
255
  ```typescript
356
- TemporalClientModule.register({
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: Buffer.from('...'),
362
- key: Buffer.from('...'),
363
- ca: Buffer.from('...'), // Optional CA certificate
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
- connectionTimeout: 10000, // 10 seconds
272
+ // For Temporal Cloud
273
+ apiKey: process.env.TEMPORAL_API_KEY,
369
274
  },
370
- namespace: 'production',
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
- - `TemporalClientModule` - Client connectivity for workflow operations
429
- - `TemporalWorkerModule` - Worker process for running activities and workflows
430
- - `TemporalScheduleModule` - Scheduling for recurring workflows
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
- - `@Query(options?)` - Marks a method as a query handler
439
- - `@Signal(options?)` - Marks a method as a signal handler
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<T, A>()` - Start a new workflow execution
311
+ - `startWorkflow()` - Start a new workflow execution
448
312
  - `signalWorkflow()` - Send a signal to a running workflow
449
- - `queryWorkflow<T>()` - Query a running workflow
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
- ## Error Handling
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
- The module includes comprehensive error handling:
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
- - Worker initialization errors are logged and handled based on configuration
482
- - Client operations include detailed error messages and proper error propagation
483
- - Activities and workflow errors are properly captured and logged
484
- - Connection errors are handled gracefully with automatic cleanup
485
- - Configurable failure modes for both client and worker connections
486
- - SDK version compatibility checks for feature availability
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 heartbeats for long-running activities
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
- - Use updates for synchronous modifications
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
- - Use retry policies for transient failures
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
- - Enable V8 context reuse for better performance
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
- - Use the WorkerManager service for controlling worker lifecycle
637
+ - Consider using OnModuleInit to set up schedules
525
638
 
526
- 6. **Security**
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