Vetsina produkcnich aplikaci potrebuje praci, ktera nezapadne do cyklu pozadavek/odpoved: posilani emailu, zpracovani uploadu, spousteni AI pipelines, synchronizace dat tretich stran, generovani reportu. Tradicni odpoved je fronta (Redis, SQS, RabbitMQ), flotila workeru, planovac a krechka hromada lepidlovych kodu, ktery se rozpadne pri kazdem deployi.
Trigger.dev zhusti tento stack do jedineho TypeScript SDK. Pisete funkce, volate je odkudkoliv a platforma se stara o frontoupravu, retries, observability, planovani a trvale provadeni. Ulohy bezi tak dlouho, jak je potreba - zadny 10sekundovy serverless timeout, zadna ztracena prace pri redeploy.
Proc Trigger.dev
Posun v roce 2026 je trvale provadeni. Workflows musi prezit restarty, krachy, deploye a rate limity. Musi take streamovat pokrok do UI v realnem case a pozastavit se pro lidsky vstup. Trigger.dev byl prebudovan kolem techto pozadavku ve verzi 3 a pokracuje v rozsireni sve AI infrastruktury.
Model je jednoduchy: definujete tasks jako exporty, SDK je vyzvedne, platforma je planuje a spousti v izolovanych kontejnerech a stav run je zachovan, abyste mohli pokracovat, opakovat a sledovat.
Zacnete
Inicializujte projekt
npx trigger.dev@latest login npx trigger.dev@latest init
To vytvori soubor trigger.config.ts a adresar trigger/ s ukazkovymi tasks. Konfiguracni soubor je zdrojem pravdy pro vas projekt: ktere adresare obsahuji tasks, nastaveni buildu, lifecycle hooks a runtime moznosti.
// trigger.config.ts import { defineConfig } from "@trigger.dev/sdk"; export default defineConfig({ project: "proj_abc123", runtime: "node", logLevel: "log", maxDuration: 3600, retries: { enabledInDev: true, default: { maxAttempts: 3, factor: 2, minTimeoutInMs: 1000, maxTimeoutInMs: 30_000, }, }, dirs: ["./trigger"], });
Spustte tasks lokalne
npx trigger.dev@latest dev
Dev server se pripoji k cloudu, zaregistruje vase tasks a streamuje runs pres vas lokalni kod. Nastavujete breakpointy ve svem editoru a trefite je na skutecnych triggerech - stejna smycka, kterou byste pouzili v jakemkoli normalnim Node.js projektu.
Definovani Task
Task je objekt exportovany s unikatnim id a funkci run. SDK kontroluje exporty v dirs a registruje je automaticky.
// trigger/send-welcome-email.ts import { task } from "@trigger.dev/sdk"; import { Resend } from "resend"; const resend = new Resend(process.env.RESEND_API_KEY); export const sendWelcomeEmail = task({ id: "send-welcome-email", retry: { maxAttempts: 5, factor: 1.8, minTimeoutInMs: 500, maxTimeoutInMs: 30_000, }, run: async (payload: { email: string; name: string }) => { const { data, error } = await resend.emails.send({ from: "hello@spinny.dev", to: payload.email, subject: `Welcome, ${payload.name}`, html: `<p>Glad you are here, ${payload.name}.</p>`, }); if (error) throw error; return { messageId: data?.id }; }, });
Tri veci k povsimnuti:
- Zadny timeout v tele run. Platforma spravuje cas provadeni pres
maxDurationv konfiguraci, ne v runtime. - Throws jsou retries. SDK zachycuje vyjimky a opakuje s exponencialnim backoff podle politiky
retry. - Navratova hodnota je zachovana. Ostatni tasks a vas frontend mohou cist
run.outputodkudkoliv.
Triggerovani Tasks
Volate task ze sveho backendu, API rout nebo jineho tasku.
import { sendWelcomeEmail } from "@/trigger/send-welcome-email"; const handle = await sendWelcomeEmail.trigger( { email: "user@example.com", name: "Alex" }, { idempotencyKey: `welcome-${userId}`, concurrencyKey: `tenant-${tenantId}`, queue: { name: "emails", concurrencyLimit: 50 }, delay: "30s", ttl: "10m", } ); console.log(handle.id); // run_xyz - pouzijte pro sledovani nebo zobrazeni pokroku
Moznosti odemykaji hodne chovani v jednom volani:
idempotencyKey- pokud run se stejnym klicem jiz existuje, SDK vrati existujici handle misto duplikace prace.concurrencyKey- serializuje runs sdileici klic, abyste neprekrocili rate limit per-tenant.queue.concurrencyLimit- globalni cap pro frontu napric vsemi klici.delay- planuje run na budouci cas.ttl- pokud run do te doby nezacal, automaticky vyprsi.
Batch trigger
Pro fan-out workloady batchTrigger prijima az 500 polozek na volani a vytvari jeden run na polozku.
await sendWelcomeEmail.batchTrigger( newUsers.map((u) => ({ payload: { email: u.email, name: u.name }, options: { idempotencyKey: `welcome-${u.id}` }, })) );
Naplanovane Tasks
Cron joby se stavaji deklaracemi prvni tridy. Schedule sam je samostatny objekt, ktery muzete pripojit k tasku vicekrat.
// trigger/daily-digest.ts import { schedules } from "@trigger.dev/sdk"; export const dailyDigest = schedules.task({ id: "daily-digest", cron: "0 9 * * *", run: async (payload) => { console.log("Scheduled at:", payload.timestamp); console.log("Last run:", payload.lastTimestamp); console.log("Timezone:", payload.timezone); console.log("Next 5 runs:", payload.upcoming); await sendDigestForDate(payload.timestamp); }, });
Pro per-tenant schedules - rekneme, jeden cron na zakaznika - je vytvarite dynamicky pres management API.
import { schedules } from "@trigger.dev/sdk"; await schedules.create({ task: "daily-digest", cron: "0 9 * * *", timezone: "America/New_York", externalId: `customer_${customerId}`, deduplicationKey: `digest-${customerId}`, });
deduplicationKey cini volani idempotentni: opakovane spusteni stejneho kodu pri deploy nestackovari duplicitni schedules.
Fronty, soubeznost a idempotence
Tri primitiva pokryvaji vetsinu potreb rate-limitingu a serazeni.
Bezny vzor: jedna fronta na tenant s malou per-key soubeznosti pro respektovani rate limitu vendora, plus klic idempotence pro zajisteni bezpecnosti retries.
await syncShopifyOrders.trigger( { shopId }, { queue: { name: `shopify-${shopId}`, concurrencyLimit: 2 }, concurrencyKey: shopId, idempotencyKey: `sync-${shopId}-${Date.now() / 60_000 | 0}`, } );
Cekani a dlouhotrvajici prace
Tasks se mohou pozastavit bez drzeni spojeni nebo spalovani compute. Platforma zachovava stav a obnovuje funkci, kdyz cekani skonci.
import { wait } from "@trigger.dev/sdk"; export const onboarding = task({ id: "onboarding", run: async (payload: { userId: string }) => { await sendWelcomeEmail.triggerAndWait({ userId: payload.userId }); await wait.for({ days: 1 }); await sendTipsEmail.trigger({ userId: payload.userId }); await wait.until({ date: oneWeekFromSignup(payload.userId) }); await sendUpgradeOffer.trigger({ userId: payload.userId }); }, });
triggerAndWait je zabijacka funkce: spusti detsky task a pozastavi rodice, dokud dite neni hotove. Komponujete tasks jako async funkce, ale orchestrace bezi trvale po dnech nebo tydnech.
Human-in-the-loop s wait.forToken
Pro schvalovaci toky a AI gates wait.forToken pozastavuje, dokud vase aplikace neodvola s vysledkem.
import { task, wait } from "@trigger.dev/sdk"; export const publishPost = task({ id: "publish-post", run: async (payload: { draftId: string }) => { const draft = await generateAIContent(payload.draftId); const token = await wait.createToken({ timeout: "7d" }); await notifyEditor({ draftId: draft.id, token: token.id }); const decision = await wait.forToken<{ approved: boolean; notes?: string }>( token.id ); if (decision.approved) { return await publish(draft); } return await applyFeedback(draft, decision.notes); }, });
Editor otevre UI, prohlizi koncept, klikne na Schvalit a vas backend dokoncuje token. Task pokracuje od mista, kde skoncil - i kdyz uplynuly hodiny nebo dny.
Lifecycle Hooks
Muzete pripojit init, onStart, onSuccess a onFailure k tasku nebo globalne v trigger.config.ts. Pouzijte je pro tracing, error reporting a sdilene nastaveni.
// trigger.config.ts export default defineConfig({ // ... init: async () => { Sentry.init({ dsn: process.env.SENTRY_DSN }); }, onFailure: async ({ error, ctx }) => { Sentry.captureException(error, { tags: { taskId: ctx.task.id, runId: ctx.run.id }, }); }, });
init bezi jednou na worker container pri bootu, ne na run, takze je to spravne misto pro nastaveni klientu a poolu.
Realtime ve Frontendu
Trigger.dev publikuje zmeny stavu run - status, metadata, output - pres streaming API. React hooks se prihlasuji k tomuto streamu a automaticky se prerenderuji.
// trigger/process-video.ts import { task, metadata } from "@trigger.dev/sdk"; export const processVideo = task({ id: "process-video", run: async (payload: { videoId: string }) => { metadata.set("stage", "transcoding"); await transcode(payload.videoId); metadata.set("stage", "thumbnails"); await generateThumbnails(payload.videoId); metadata.set("stage", "uploading"); const url = await uploadToCDN(payload.videoId); return { url }; }, });
// components/VideoStatus.tsx "use client"; import { useRealtimeRun } from "@trigger.dev/react-hooks"; import type { processVideo } from "@/trigger/process-video"; export function VideoStatus({ runId, publicAccessToken, }: { runId: string; publicAccessToken: string; }) { const { run, error } = useRealtimeRun<typeof processVideo>(runId, { accessToken: publicAccessToken, }); if (error) return <p>Error: {error.message}</p>; if (!run) return <p>Loading...</p>; return ( <div> <p>Status: {run.status}</p> <p>Stage: {String(run.metadata?.stage ?? "queued")}</p> {run.output?.url && <video src={run.output.url} controls />} </div> ); }
Generujete public access token na strane serveru, scoped na specificky run, a posilate jej klientovi. Hook spravuje auth, opakovane pripojeni a inkrementalni aktualizace.
Pro trigger-and-subscribe v jednom kroku:
import { useRealtimeTaskTrigger } from "@trigger.dev/react-hooks"; const { submit, run, isLoading } = useRealtimeTaskTrigger<typeof processVideo>( "process-video", { accessToken: publicAccessToken } ); <button onClick={() => submit({ videoId })} disabled={isLoading}> Process video </button>;
AI Agenti a Streaming
Trigger.dev se stal popularnim runtime pro AI agenty, protoze stejna primitiva - trvale provadeni, retries, cekani, real-time metadata, human-in-the-loop - jsou presne to, co agenti potrebuji. Streamujete tokeny od poskytovatele modelu do metadata, zatimco run probiha, frontend je renderuje zive a run prezije dlouhotrvajici tool calls bez spaleni serverless timeoutu.
import { task, metadata } from "@trigger.dev/sdk"; import { streamText } from "ai"; import { anthropic } from "@ai-sdk/anthropic"; export const researchAgent = task({ id: "research-agent", maxDuration: 1800, run: async (payload: { question: string }) => { const result = streamText({ model: anthropic("claude-opus-4-7"), system: "You are a research assistant. Use the web.", prompt: payload.question, tools: { webSearch }, }); let fullText = ""; for await (const chunk of result.textStream) { fullText += chunk; metadata.set("partial", fullText); } return { answer: fullText, usage: await result.usage }; }, });
Frontend pouziva useRealtimeRun a cte run.metadata.partial, aby renderoval streaming odpoved, stejnym zpusobem, jakym byste renderoval chat completion - krome toho, ze tato prezije plne nacteni stranky.
Deployment
Deploye kompiluji vase tasks do versionovaneho bundlu, sestavuji kontejner a atomicky prepinaji provoz. Stare in-flight runs nadale pouzivaji predchozi verzi.
npx trigger.dev@latest deploy --env prod
V CI typicky pripojite toto ke stejnemu workflow, ktery posila vasi aplikaci:
# .github/workflows/deploy.yml - name: Deploy Trigger.dev env: TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }} run: npx trigger.dev@latest deploy --env prod
Pro preview prostredi predejte --env preview --branch ${{ github.head_ref }} a Trigger.dev vytvori izolovane prostredi na vetev, odrazejici, jak Vercel zachazi s preview deploymenty.
Self-Hosting vs Cloud
Trigger.dev je open source pod licenci Apache 2.0. Muzete self-host na jakekoliv kontejnerove platforme (Docker Compose, Kubernetes, Fly.io) nebo pouzivat spravovany cloud na trigger.dev.
| Aspekt | Cloud | Self-hosted |
|---|---|---|
| Setup | Registrace, spusteni init | Spusteni docker-compose nebo Helm chart |
| Skalovani | Automaticke | Vase odpovednost |
| Pricing | Per run + per compute | Pouze naklady na infru |
| Compliance | SOC 2 | Cokoliv vase prostredi poskytuje |
| Idealni pro | Vetsinu tymu | Pristnou rezidenci dat, vlastni infru |
SDK a CLI jsou identicke mezi rezimy - menite profile flag a smerujete na vlastni instanci.
Best Practices
1. Drzte payloads male a serializovatelne
Predavejte ID a reference, ne plne objekty. Tahnete data uvnitr tasku. To drzi frontu malou, payloads levne k logovani a umoznuje vam menit zdroj dat bez re-triggerovani.
2. Idempotency keys na kazdem externim volani
Kombinujte idempotencyKey na trigger tasku s idempotency klici na vendor API (Stripe, OpenAI, atd.). Retries budou bezpecne end-to-end.
3. Pouzivejte triggerAndWait pro orchestraci, ne Promise.all triggeru
Rodic, ktery vola triggerAndWait, trvale komponuje detske tasks. Rodic, ktery triggerne a hned vyresi, ztraci pozorovatelnost retezu.
4. Tagujte runs
Pridejte tags k triggerum (tags: ["user:123", "feature:onboarding"]), abyste mohli filtrovat dashboard a management API podle business dimenzi.
5. Drzte init idempotentni
Bezi pri kazdem cold startu. Vyhnete se tam migracim nebo jednorazovym side effects.
Zaver
Trigger.dev odstranuje kategorie prace, ktere drive vyzadovaly stavbu job systemu od nuly. Pisete async TypeScript, volate jej odkudkoliv a platforma vam dava trvale provadeni, planovani, fronty, retries, real-time aktualizace a vzory human-in-the-loop out of the box.
Stejna povrch, ktera pohani nocni cron, je povrch, ktera pohani multi-step AI agenta, ktery streamuje do frontendu a pozastavuje se pro review. Tato konvergence je to, co cini framework hodnym vazneho pohledu v roce 2026, at uz provozujete SaaS, ktery potrebuje spolehlivou praci na pozadi, nebo dodavate AI funkce, ktere prezivaji serverless timeout.
Checklist Zacatku:
- Zaregistrujte se na trigger.dev nebo spustte self-hosted Docker stack
npx trigger.dev@latest initve vasem projektu- Definujte svuj prvni task s
task({ id, run })- Triggerne jej z vaseho API a sledujte run v dashboardu
- Pridejte
idempotencyKeyaconcurrencyKeypro produkcni bezpecnost- Pripojte
useRealtimeRunk status komponente- Deployujte s
trigger.dev deploy --env prodz CI