Hau het cac ung dung production deu can cong viec khong vua voi chu ky request/response: gui email, xu ly upload, chay AI pipeline, dong bo du lieu ben thu ba, tao bao cao. Cau tra loi truyen thong la mot queue (Redis, SQS, RabbitMQ), mot doi worker, mot scheduler va mot dong code keo dan mong manh bi vo o moi lan deploy.
Trigger.dev thu gon stack do thanh mot SDK TypeScript duy nhat. Ban viet cac function, goi chung tu bat ky dau, va nen tang xu ly queueing, retries, observability, lap lich va thuc thi ben vung. Tasks chay bao lau tuy can - khong co timeout serverless 10 giay, khong mat cong viec khi redeploy.
Tai sao Trigger.dev
Su thay doi nam 2026 la thuc thi ben vung. Workflow phai song sot qua khoi dong lai, su co, deploy va rate limit. Chung cung phai stream tien trinh den UI theo thoi gian thuc va tam dung de nhan input cua con nguoi. Trigger.dev duoc xay dung lai xung quanh nhung yeu cau nay voi phien ban 3 va tiep tuc mo rong be mat ha tang AI.
Mo hinh don gian: ban dinh nghia tasks la exports, SDK chon chung, nen tang lap lich va chay chung trong cac container co lap, va trang thai run duoc giu lai de ban co the tiep tuc, thu lai va quan sat.
Bat dau
Khoi tao mot du an
npx trigger.dev@latest login npx trigger.dev@latest init
Dieu nay tao mot tep trigger.config.ts va mot thu muc trigger/ voi tasks vi du. Tep config la nguon su that cho du an cua ban: thu muc nao chua tasks, cai dat build, lifecycle hooks va tuy chon runtime.
// 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"], });
Chay tasks tai dia phuong
npx trigger.dev@latest dev
Server dev ket noi voi cloud, dang ky tasks cua ban, va stream runs qua code dia phuong cua ban. Ban dat breakpoint trong editor cua minh va dat den chung tren cac trigger thuc te - cung mot vong lap ma ban se su dung trong bat ky du an Node.js binh thuong nao.
Dinh nghia mot Task
Task la mot doi tuong duoc xuat voi mot id duy nhat va mot ham run. SDK kiem tra cac xuat trong dirs va dang ky chung tu dong.
// 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 }; }, });
Ba dieu can chu y:
- Khong co timeout trong than run. Nen tang quan ly thoi gian thuc thi qua
maxDurationtrong config, khong phai trong runtime. - Throws la retries. SDK bat exception va chay lai voi exponential backoff theo chinh sach
retry. - Gia tri tra ve duoc giu lai. Cac tasks khac va frontend cua ban co the doc
run.outputtu bat ky dau.
Trigger Tasks
Ban goi mot task tu backend, route API hoac task khac.
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 - su dung de theo doi hoac hien thi tien trinh
Cac tuy chon mo khoa nhieu hanh vi trong mot lenh goi:
idempotencyKey- neu mot run voi cung key da ton tai, SDK tra ve handle hien co thay vi nhan ban cong viec.concurrencyKey- tuan tu hoa cac runs chia se key de ban khong vuot qua rate limit moi tenant.queue.concurrencyLimit- cap toan cau cho queue tren tat ca cac key.delay- lap lich run cho mot thoi diem trong tuong lai.ttl- neu run chua bat dau vao thoi diem do, het han tu dong.
Batch trigger
Cho cac workload fan-out, batchTrigger chap nhan toi 500 muc moi cuoc goi va tao mot run moi muc.
await sendWelcomeEmail.batchTrigger( newUsers.map((u) => ({ payload: { email: u.email, name: u.name }, options: { idempotencyKey: `welcome-${u.id}` }, })) );
Tasks lap lich
Cron jobs tro thanh khai bao hang nhat. Schedule la mot doi tuong rieng biet ma ban co the gan vao mot task nhieu lan.
// 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); }, });
Cho cac schedule moi tenant - chang han, mot cron moi khach hang - ban tao chung dong qua 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 lam cho cuoc goi idempotent: chay lai cung mot code khi deploy khong xep chong cac schedule trung lap.
Queues, Concurrency va Idempotency
Ba primitive bao phu hau het nhu cau rate-limiting va sap xep.
Mot mau pho bien: mot queue moi tenant voi concurrency moi key nho de ton trong rate limit cua vendor, cong voi mot idempotency key de lam cho retry an toan.
await syncShopifyOrders.trigger( { shopId }, { queue: { name: `shopify-${shopId}`, concurrencyLimit: 2 }, concurrencyKey: shopId, idempotencyKey: `sync-${shopId}-${Date.now() / 60_000 | 0}`, } );
Waits va Cong viec dai han
Tasks co the tam dung ma khong giu ket noi hoac dot compute. Nen tang giu trang thai va tiep tuc ham khi wait hoan thanh.
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 la tinh nang sat thu: no trigger mot task con va treo parent cho den khi child hoan thanh. Ban soan tasks nhu cac ham async, nhung orchestration chay ben vung qua nhieu ngay hoac tuan.
Human-in-the-loop voi wait.forToken
Cho luong phe duyet va AI gates, wait.forToken tam dung cho den khi ung dung cua ban callback voi mot ket qua.
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 mo mot UI, xem xet ban nhap, nhap Phe duyet va backend cua ban hoan thanh token. Task tiep tuc tu noi no dung lai - ngay ca khi nhieu gio hoac ngay da troi qua.
Lifecycle Hooks
Ban co the gan init, onStart, onSuccess va onFailure vao mot task hoac toan cuc trong trigger.config.ts. Su dung chung cho tracing, error reporting va setup chia se.
// 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 chay mot lan moi worker container khi boot, khong phai moi run, vi vay no la noi thich hop de thiet lap clients va pools.
Realtime trong Frontend
Trigger.dev xuat ban thay doi trang thai run - status, metadata, output - qua mot streaming API. React hooks subscribe vao stream do va re-render tu dong.
// 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> ); }
Ban tao public access token o phia server, voi pham vi cho mot run cu the, va gui den client. Hook xu ly auth, ket noi lai va cap nhat tang dan.
Cho trigger-and-subscribe trong mot buoc:
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 Agents va Streaming
Trigger.dev da tro thanh mot runtime pho bien cho AI agents vi cac primitive giong nhau - thuc thi ben vung, retries, waits, metadata thoi gian thuc, human-in-the-loop - chinh xac la nhung gi agents can. Ban stream token tu mot model provider vao metadata trong khi run dien ra, frontend render chung truc tiep va run song sot qua tool calls dai han ma khong dot timeout serverless.
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 su dung useRealtimeRun va doc run.metadata.partial de render phan hoi streaming, theo cung mot cach ban se render mot chat completion - tru rang cai nay song sot qua mot reload trang day du.
Deploying
Deploys bien dich tasks cua ban thanh mot bundle co phien ban, xay dung mot container va swap traffic mot cach atomic. Cac runs cu dang chay tiep tuc su dung phien ban truoc.
npx trigger.dev@latest deploy --env prod
Trong CI ban thuong noi day no vao cung mot workflow chuyen ung dung cua ban:
# .github/workflows/deploy.yml - name: Deploy Trigger.dev env: TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }} run: npx trigger.dev@latest deploy --env prod
Cho moi truong preview, truyen --env preview --branch ${{ github.head_ref }} va Trigger.dev tao mot moi truong co lap moi branch, phan anh cach Vercel xu ly preview deployment.
Self-Hosting vs Cloud
Trigger.dev la ma nguon mo theo giay phep Apache 2.0. Ban co the self-host tren bat ky nen tang container nao (Docker Compose, Kubernetes, Fly.io) hoac su dung cloud quan ly tai trigger.dev.
| Khia canh | Cloud | Self-hosted |
|---|---|---|
| Setup | Dang ky, chay init | Chay docker-compose hoac Helm chart |
| Scaling | Tu dong | Trach nhiem cua ban |
| Pricing | Moi run + moi compute | Chi chi phi infra |
| Compliance | SOC 2 | Bat ky gi moi truong cua ban cung cap |
| Tot nhat cho | Hau het cac doi | Cu tru du lieu nghiem ngat, infra tuy chinh |
SDK va CLI giong nhau giua cac che do - ban thay doi mot co profile va tro den instance cua rieng ban.
Best Practices
1. Giu payloads nho va co the serializable
Truyen ID va tham chieu, khong phai doi tuong day du. Keo du lieu ben trong task. Dieu nay giu queue nho, payloads re de log va cho phep ban thay doi nguon du lieu ma khong can re-trigger.
2. Idempotency keys tren moi cuoc goi ben ngoai
Ket hop idempotencyKey tren task trigger voi idempotency keys tren API cua vendor (Stripe, OpenAI, vv). Retries se an toan tu dau den cuoi.
3. Su dung triggerAndWait cho orchestration, khong phai Promise.all cua triggers
Mot parent goi triggerAndWait ben vung soan thao tasks con. Mot parent trigger va resolve ngay lap tuc mat kha nang quan sat cua chuoi.
4. Tag runs
Them tags vao triggers (tags: ["user:123", "feature:onboarding"]) de ban co the loc dashboard va management API theo cac chieu kinh doanh.
5. Giu init idempotent
No chay tren moi cold start. Tranh migrations hoac side effect mot lan o do.
Ket luan
Trigger.dev loai bo cac danh muc cong viec ma truoc day yeu cau xay dung mot he thong job tu dau. Ban viet TypeScript async, goi no tu bat ky dau va nen tang cung cap cho ban thuc thi ben vung, lap lich, queues, retries, cap nhat thoi gian thuc va cac mau human-in-the-loop ngay tu dau.
Cung mot be mat chay mot cron ban dem la be mat chay mot AI agent nhieu buoc stream den frontend va tam dung de xem xet. Su hoi tu do la dieu khien framework dang xem xet nghiem tuc trong nam 2026, du ban co dang chay mot SaaS can cong viec nen dang tin cay hay van chuyen cac tinh nang AI song sot qua mot timeout serverless.
Checklist Bat dau:
- Dang ky tai trigger.dev hoac chay stack Docker self-hosted
npx trigger.dev@latest inittrong du an cua ban- Dinh nghia task dau tien voi
task({ id, run })- Trigger no tu API va xem run trong dashboard
- Them
idempotencyKeyvaconcurrencyKeycho an toan production- Noi day
useRealtimeRunvao mot status component- Deploy voi
trigger.dev deploy --env prodtu CI