Runtime decorators
Beyond core @Api, @Database, @Storage, and @Webhook, @ductape/nestjs exposes decorators for events, notifications, features, agents, sessions, and resilience. All runtime decorators are executed by DuctapeMethodInterceptor in integration mode.
The first method argument becomes SDK input / message (same convention as @Api), except for @Events.Consumer which receives incoming messages.
Events, notifications, cache, graph, vector, feature, jobs, secrets
Method decorators
| Decorator | SDK call |
|---|---|
@Events.Produce({ event, product?, env?, session?, cache? }) | events.produce |
@Events.Dispatch({ broker, event, schedule?, … }) | events.dispatch |
@Events.Consumer({ event, product?, env? }) | events.consume (wired at startup) |
@Notification.Send({ notification, event?, channel?, … }) | notifications.send / channel helpers |
@Notification.Dispatch({ notification, event, schedule?, … }) | notifications.dispatch |
@Feature.Execute({ tag, product?, env? }) | feature.execute |
@Feature.Dispatch({ tag, schedule?, … }) | feature.dispatch |
@Job.Run({ event, retries?, start_at?, … }) | processor processJob |
@Secret('KEY') on constructor param | inject SecretHandle (via DuctapeSecretsModule) |
Injectable handles
| Module | Decorator | Handle |
|---|---|---|
DuctapeCacheModule.register({ tags: ['user-cache'] }) | @Cache('user-cache') | CacheHandle |
DuctapeGraphModule.register({ tags: ['social'] }) | @Graph('social') | GraphHandle |
DuctapeVectorModule.register({ tags: ['embeddings'] }) | @Vector('embeddings') | VectorHandle |
DuctapeSecretsModule.register({ keys: ['API_KEY'] }) | @Secret('API_KEY') | SecretHandle |
Producing events
@Events.Produce publishes the returned value as the event message immediately:
import { Injectable } from '@nestjs/common';
import { Events } from '@ductape/nestjs';
@Injectable()
export class OrdersService {
@Events.Produce({ event: 'order-events:order-created' })
emitOrderCreated(payload: { orderId: string; total: number }) {
return payload;
}
}
Calling ordersService.emitOrderCreated(payload) publishes payload to the topic and returns the SDK result.
Dispatching (scheduled) events
@Events.Dispatch schedules a publish through a Ductape job. The schedule can be static (fixed in the decorator) or dynamic (derived from the method return value):
import { Injectable } from '@nestjs/common';
import { Events } from '@ductape/nestjs';
@Injectable()
export class SchedulerService {
// Static schedule — known at deploy time:
@Events.Dispatch({
broker: 'order-events',
event: 'order-events:reminder-due',
schedule: { every: 86400000 }, // every 24 h
})
scheduleReminder(payload: { message: { orderId: string } }) {
return payload;
}
// Dynamic schedule — derived from data at call time:
@Events.Dispatch({ broker: 'order-events', event: 'order-events:fulfillment-due' })
scheduleFulfillment(order: { id: string; items: unknown[]; expectedAt: number }) {
return {
message: { orderId: order.id, items: order.items },
schedule: { start_at: order.expectedAt },
retries: 3,
};
}
}
When the method return value contains a message key, the decorator reads message, schedule, and retries from it — overriding anything specified in the decorator. If the return value has no message key, the first argument is used as-is (backward-compatible with static-schedule usage).
For schedules where even the broker or event must vary at call time, use ctx.sdk.events.dispatch() directly.
Consuming events
@Events.Consumer registers a subscription that fires for each incoming message. No onModuleInit boilerplate is required — DuctapeModule wires it up automatically at startup:
import { Injectable } from '@nestjs/common';
import { Events } from '@ductape/nestjs';
@Injectable()
export class OrderConsumerService {
@Events.Consumer({ event: 'order-events:order-created' })
async onOrderCreated(message: { orderId: string; total: number }) {
await this.processOrder(message);
// Return to acknowledge; throw to nack (triggers retry / DLQ)
}
private async processOrder(message: { orderId: string; total: number }) {
// business logic
}
}
The product and env used are the defaults from DuctapeModule.forIntegration(...). Override them per method if needed:
@Events.Consumer({ event: 'order-events:order-created', product: 'my-product', env: 'prd' })
Feature execution
import { Injectable } from '@nestjs/common';
import { Feature } from '@ductape/nestjs';
@Injectable()
export class FulfillmentService {
@Feature.Execute({ tag: 'fulfillment' })
runFulfillment(input: Record<string, unknown>) {
return input;
}
}
Related platform docs: Message brokers, Notifications, Jobs, Graphs, Vectors.
Agents, warehouse, sessions, health, quota, fallback
Method decorators
| Decorator | SDK call |
|---|---|
@Agent.Run({ tag, product?, env?, session?, cache? }) | agents.getService().run() |
@Agent.Dispatch({ tag, schedule?, retries?, … }) | agents.getService().dispatch() |
@Warehouse.Query({ product?, env? }) | warehouse.query — method arg is query object |
@Session.Start({ tag, … }) | sessions.start |
@Session.Verify({ tag, … }) | sessions.verify — body { token } |
@Session.Refresh({ tag, … }) | sessions.refresh — body { refreshToken } |
@Session.Revoke({ tag, … }) | sessions.revoke |
@Health.Run({ tag, … }) | health.run |
@Health.Status({ tag, … }) | health.status |
@Quota.Run({ tag, … }) | quota.run |
@Quota.Dispatch({ tag, schedule?, … }) | quota.dispatch |
@Fallback.Run({ tag, … }) | fallback.run |
@Fallback.Dispatch({ tag, schedule?, … }) | fallback.dispatch |
Injectable handles
| Module | Decorator | Handle |
|---|---|---|
DuctapeAgentModule.register({ tags: ['support'] }) | @Agent('support') | AgentHandle (.run(), .dispatch()) |
DuctapeWarehouseModule.register() | @Warehouse() | WarehouseHandle (.query(), .select(), …) |
Example:
@Injectable()
export class SupportService {
constructor(@Agent('support') private readonly agent: AgentHandle) {}
@Session.Start({ tag: 'user-session' })
startSession(credentials: { userId: string }) {
return credentials;
}
async ask(question: string) {
return this.agent.run({ input: { question } });
}
}
Related platform docs: Sessions, Quotas, Fallbacks, Warehouse.
Unwrapped SDK APIs
Use @InjectContext() and ctx.sdk.* for modules without Nest decorators yet (logs, cloud, operators, …).
Related
- Core decorators
- Module setup — feature module registration
- Interceptors