Skip to Content
AppSDKExternal TasksHandler entwickeln

Handler entwickeln

External Task Handler werden als Dateien im App-Verzeichnis von Next.js angelegt. Das App-SDK erkennt sie automatisch und startet Worker-Prozesse dafür.

Dateikonvention

Der Dateiname muss external_task.ts oder external_task.js lauten. Die Datei wird in einem Unterverzeichnis von app/ platziert. Der Verzeichnisname bestimmt das Topic.

      • external_task.ts
      • external_task.ts
      • external_task.ts

Diese Struktur erzeugt drei Worker für die Topics filter_email, forward_email und delete_email.

Pro Verzeichnis darf nur eine External-Task-Datei existieren. Werden sowohl external_task.ts als auch external_task.js gefunden, wird kein Worker gestartet.

Topic-Ableitung

Das Topic ergibt sich aus dem relativen Pfad des Verzeichnisses zum App-Ordner:

DateipfadTopic
app/filter_email/external_task.tsfilter_email
app/orders/validate/external_task.tsorders/validate
app/(workers)/send_email/external_task.tssend_email

Route Groups  (Verzeichnisse in Klammern wie (workers)) werden aus dem Topic entfernt. Siehe Erweiterte Konzepte.

Handler-Signatur

Die Handler-Funktion muss als default-Export bereitgestellt werden:

export default async function handler( payload: TPayload, externalTask?: ExternalTask<TPayload>, signal?: AbortSignal ): Promise<TResult>

Parameter

ParameterTypBeschreibung
payloadTPayloadDie Prozessdaten (Token) des External Service Tasks
externalTaskExternalTask<TPayload> (optional)Das vollständige External-Task-Objekt mit Metadaten
signalAbortSignal (optional)Signal zum Abbruch bei Boundary Events

Rückgabewert

Der Rückgabewert wird als Ergebnis-Token des Service Tasks in den Prozess geschrieben. Die Engine nutzt diesen Wert für nachfolgende Gateways und Aktivitäten.

Beispiele

Einfacher Handler

app/filter_email/external_task.ts
export default async function filterEmail(payload: any) { const isSpam = payload.subject?.toLowerCase().includes('spam'); return { is_spam: isSpam }; }

Handler mit typisierten Payload

app/calculate_price/external_task.ts
interface PricePayload { productId: string; quantity: number; discount?: number; } interface PriceResult { totalPrice: number; currency: string; } export default async function calculatePrice( payload: PricePayload ): Promise<PriceResult> { const basePrice = await fetchProductPrice(payload.productId); const total = basePrice * payload.quantity * (1 - (payload.discount ?? 0)); return { totalPrice: total, currency: 'EUR', }; } async function fetchProductPrice(productId: string): Promise<number> { // Preisermittlung aus Datenbank oder API return 29.99; }

Handler mit ExternalTask-Objekt

app/audit_log/external_task.ts
import { ExternalTask } from '@5minds/processcube_engine_sdk'; interface AuditPayload { action: string; userId: string; } export default async function auditLog( payload: AuditPayload, externalTask?: ExternalTask<AuditPayload> ) { console.log(`Audit: ${payload.action} von ${payload.userId}`); console.log(`Prozess-Instanz: ${externalTask?.processInstanceId}`); console.log(`Correlation: ${externalTask?.correlationId}`); return { logged: true }; }

Handler mit AbortSignal

app/long_running_task/external_task.ts
export const config = { lockDuration: 5000, }; export default async function longRunningTask( payload: any, _task: any, signal: AbortSignal ) { signal.addEventListener('abort', () => { console.log('Task wurde durch Boundary Event abgebrochen'); }, { once: true }); // Langwierige Operation mit Abbruch-Unterstützung const result = await fetchWithTimeout(payload.url, signal); if (signal.aborted) return; return { data: result }; } async function fetchWithTimeout(url: string, signal: AbortSignal) { const response = await fetch(url, { signal }); return response.json(); }

Fehlerbehandlung

Wirft der Handler einen Fehler, wird dieser als BPMN-Error an die Engine gemeldet. Ein angehängtes Error-Boundary-Event kann den Fehler im Prozess behandeln.

app/validate_order/external_task.ts
export default async function validateOrder(payload: any) { if (!payload.orderId) { throw new Error('Keine Order-ID vorhanden'); } const order = await loadOrder(payload.orderId); if (!order) { throw new Error(`Order ${payload.orderId} nicht gefunden`); } return { valid: true, orderTotal: order.total }; } async function loadOrder(orderId: string) { // Bestellung aus Datenbank laden return { total: 99.99 }; }

Bei einem Fehler wird der Worker nicht beendet. Er verarbeitet weiterhin neue Tasks. Nur bei kritischen Fehlern (z.B. Verbindungsabbruch) greift der automatische Neustart.