زیادہ تر پروڈکشن ایپلی کیشنز کو ایسے کام کی ضرورت ہوتی ہے جو request/response cycle میں فٹ نہیں ہوتا: ای میلز بھیجنا، اپلوڈز پر کارروائی کرنا، AI pipelines چلانا، تیسرے فریق کا ڈیٹا synchronize کرنا، رپورٹس بنانا۔ روایتی جواب ایک queue (Redis، SQS، RabbitMQ)، ایک worker fleet، ایک scheduler، اور glue code کا ایک نازک ڈھیر ہے جو ہر deploy پر ٹوٹ جاتا ہے۔
Trigger.dev اس stack کو ایک واحد TypeScript SDK میں سمیٹ دیتا ہے۔ آپ functions لکھتے ہیں، انہیں کہیں سے بھی call کرتے ہیں، اور platform queueing، retries، observability، scheduling اور پائیدار execution سنبھالتا ہے۔ Tasks جتنی دیر تک ضرورت ہو چلتے ہیں - کوئی 10 سیکنڈ serverless timeout نہیں، redeploys پر کوئی کھویا ہوا کام نہیں۔
کیوں Trigger.dev
2026 کی تبدیلی پائیدار execution ہے۔ Workflows کو restarts، crashes، deploys اور rate limits سے بچنا ہوگا۔ انہیں progress کو realtime میں UI پر stream اور انسانی input کے لیے pause بھی کرنا ہوگا۔ Trigger.dev ورژن 3 کے ساتھ ان تقاضوں کے گرد دوبارہ تعمیر کیا گیا اور اپنے AI infrastructure surface کو وسیع کرتا رہتا ہے۔
ماڈل سادہ ہے: آپ tasks کو exports کے طور پر definite کرتے ہیں، SDK انہیں اٹھاتا ہے، platform انہیں isolated containers میں schedule اور run کرتا ہے اور run state برقرار رہتی ہے تاکہ آپ resume، retry اور observe کر سکیں۔
شروعات
پراجیکٹ initialize کریں
npx trigger.dev@latest login npx trigger.dev@latest init
یہ ایک trigger.config.ts فائل اور مثال tasks کے ساتھ ایک trigger/ directory بناتا ہے۔ Config فائل آپ کے پراجیکٹ کے لیے سچائی کا ذریعہ ہے: کون سی directories میں tasks ہیں، build settings، lifecycle hooks اور runtime options۔
// 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"], });
Tasks مقامی طور پر چلائیں
npx trigger.dev@latest dev
dev server cloud سے connect ہوتا ہے، آپ کے tasks register کرتا ہے اور آپ کے local code کے ذریعے runs stream کرتا ہے۔ آپ اپنے editor میں breakpoints سیٹ کرتے ہیں اور انہیں حقیقی triggers پر hit کرتے ہیں - وہی loop جو آپ کسی بھی عام Node.js پراجیکٹ میں استعمال کرتے ہیں۔
ایک Task کی تعریف
Task ایک exported object ہے جس کا منفرد id اور run function ہوتا ہے۔ SDK dirs کے ذریعے exports کا معائنہ کرتا ہے اور انہیں خودکار طور پر register کرتا ہے۔
// 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 }; }, });
نوٹ کرنے کی تین چیزیں:
- Run body میں کوئی timeout نہیں۔ Platform runtime میں نہیں، config میں
maxDurationکے ذریعے execution time کو منظم کرتا ہے۔ - Throws retries ہیں۔ SDK exceptions پکڑتا ہے اور
retrypolicy کے مطابق exponential backoff کے ساتھ دوبارہ چلاتا ہے۔ - Return value برقرار رہتی ہے۔ دوسرے tasks اور آپ کا frontend کہیں سے بھی
run.outputپڑھ سکتے ہیں۔
Tasks کو trigger کرنا
آپ اپنے backend، اپنے API routes یا کسی اور task سے ایک task کو call کرتے ہیں۔
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 - اس کا استعمال progress کو track یا display کرنے کے لیے کریں
Options ایک ہی call میں بہت ساری behavior unlock کرتی ہیں:
idempotencyKey- اگر اسی key کے ساتھ run پہلے سے موجود ہے، تو SDK کام کو duplicate کرنے کی بجائے موجودہ handle لوٹا دیتا ہے۔concurrencyKey- key share کرنے والے runs کو serialize کرتا ہے تاکہ آپ per-tenant rate limit سے زیادہ نہ ہوں۔queue.concurrencyLimit- تمام keys میں queue کے لیے global cap۔delay- run کو مستقبل کے وقت کے لیے schedule کرتا ہے۔ttl- اگر run تب تک شروع نہیں ہوا ہے، تو خودکار طور پر expire کر دیتا ہے۔
Batch trigger
Fan-out workloads کے لیے، batchTrigger فی call 500 items تک قبول کرتا ہے اور فی item ایک run بناتا ہے۔
await sendWelcomeEmail.batchTrigger( newUsers.map((u) => ({ payload: { email: u.email, name: u.name }, options: { idempotencyKey: `welcome-${u.id}` }, })) );
شیڈولڈ Tasks
Cron jobs first-class declarations بن جاتے ہیں۔ Schedule خود ایک علیحدہ object ہے جسے آپ کسی task پر متعدد بار attach کر سکتے ہیں۔
// 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); }, });
Per-tenant schedules کے لیے - مان لیں، فی customer ایک cron - آپ انہیں 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 call کو idempotent بناتا ہے: deploy time پر وہی code دوبارہ چلانا duplicate schedules کو نہیں روکتا۔
Queues، Concurrency اور Idempotency
تین primitives زیادہ تر rate-limiting اور ordering کی ضروریات کا احاطہ کرتے ہیں۔
ایک عام pattern: vendor کی rate limit کا احترام کرنے کے لیے فی tenant ایک queue چھوٹی per-key concurrency کے ساتھ، نیز retries کو محفوظ بنانے کے لیے ایک idempotency key۔
await syncShopifyOrders.trigger( { shopId }, { queue: { name: `shopify-${shopId}`, concurrencyLimit: 2 }, concurrencyKey: shopId, idempotencyKey: `sync-${shopId}-${Date.now() / 60_000 | 0}`, } );
Waits اور طویل دورانیے کا کام
Tasks ایک connection برقرار رکھے یا compute جلائے بغیر pause کر سکتے ہیں۔ Platform state کو برقرار رکھتا ہے اور wait مکمل ہونے پر function کو دوبارہ شروع کرتا ہے۔
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 killer feature ہے: یہ ایک child task کو trigger کرتا ہے اور child مکمل ہونے تک parent کو suspend کرتا ہے۔ آپ tasks کو async functions کی طرح compose کرتے ہیں، لیکن orchestration دنوں یا ہفتوں تک پائیدار طور پر چلتا ہے۔
wait.forToken کے ساتھ Human-in-the-loop
Approval flows اور AI gates کے لیے، wait.forToken تب تک pause کرتا ہے جب تک کہ آپ کی application نتیجے کے ساتھ callback نہ کرے۔
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 ایک UI کھولتا ہے، draft کا جائزہ لیتا ہے، Approve پر click کرتا ہے، اور آپ کا backend token مکمل کرتا ہے۔ Task جہاں چھوڑا تھا وہاں سے continue ہوتا ہے - چاہے گھنٹے یا دن گزر گئے ہوں۔
Lifecycle Hooks
آپ init، onStart، onSuccess اور onFailure کو ایک task یا global طور پر trigger.config.ts میں attach کر سکتے ہیں۔ tracing، error reporting اور shared setup کے لیے استعمال کریں۔
// 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 boot پر فی worker container ایک بار چلتا ہے، فی run نہیں، لہذا یہ clients اور pools سیٹ اپ کرنے کے لیے صحیح جگہ ہے۔
Frontend میں Realtime
Trigger.dev streaming API پر run state changes - status، metadata، output - شائع کرتا ہے۔ React hooks اس stream کو subscribe کرتے ہیں اور خودکار طور پر re-render کرتے ہیں۔
// 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> ); }
آپ مخصوص run کے لیے scoped server-side public access token تیار کرتے ہیں اور اسے client کو بھیجتے ہیں۔ Hook auth، reconnection اور incremental updates کو سنبھالتا ہے۔
ایک قدم میں trigger-and-subscribe کے لیے:
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 اور Streaming
Trigger.dev AI agents کے لیے ایک مقبول runtime بن گیا ہے کیونکہ وہی primitives - پائیدار execution، retries، waits، realtime metadata، human-in-the-loop - بالکل وہی ہیں جن کی agents کو ضرورت ہے۔ آپ run کے دوران model provider سے tokens کو metadata میں stream کرتے ہیں، frontend انہیں live render کرتا ہے، اور run serverless timeout جلائے بغیر طویل دورانیے کے tool calls سے بچ جاتا ہے۔
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 useRealtimeRun استعمال کرتا ہے اور streaming response render کرنے کے لیے run.metadata.partial پڑھتا ہے، اسی طرح جیسے آپ chat completion render کرتے - سوائے اس کے کہ یہ مکمل page reload سے بچ جاتا ہے۔
Deploying
Deploys آپ کے tasks کو ایک versioned bundle میں compile کرتے ہیں، ایک container بناتے ہیں اور traffic کو atomic طور پر swap کرتے ہیں۔ پرانے in-flight runs پچھلا version استعمال کرتے رہتے ہیں۔
npx trigger.dev@latest deploy --env prod
CI میں آپ عام طور پر اسے اسی workflow میں جوڑتے ہیں جو آپ کی app بھیجتا ہے:
# .github/workflows/deploy.yml - name: Deploy Trigger.dev env: TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }} run: npx trigger.dev@latest deploy --env prod
Preview environments کے لیے، --env preview --branch ${{ github.head_ref }} پاس کریں اور Trigger.dev فی branch ایک isolated environment بناتا ہے، Vercel کس طرح preview deployments کو سنبھالتا ہے اس کی عکاسی کرتا ہے۔
Self-Hosting بمقابلہ Cloud
Trigger.dev Apache 2.0 license کے تحت open source ہے۔ آپ کسی بھی container platform (Docker Compose، Kubernetes، Fly.io) پر self-host کر سکتے ہیں یا trigger.dev پر managed cloud استعمال کر سکتے ہیں۔
| پہلو | Cloud | Self-hosted |
|---|---|---|
| Setup | سائن اپ، init چلائیں | docker-compose یا Helm chart چلائیں |
| Scaling | خودکار | آپ کی ذمہ داری |
| Pricing | فی run + فی compute | صرف infra لاگت |
| Compliance | SOC 2 | جو آپ کا environment فراہم کرتا ہے |
| بہترین | زیادہ تر teams | سخت data residency، custom infra |
SDK اور CLI modes کے درمیان یکساں ہیں - آپ ایک profile flag تبدیل کرتے ہیں اور اپنے instance کی طرف اشارہ کرتے ہیں۔
Best Practices
1. payloads کو چھوٹا اور serializable رکھیں
IDs اور references پاس کریں، مکمل objects نہیں۔ task کے اندر data کھینچیں۔ یہ queue کو چھوٹا رکھتا ہے، payloads log کرنے کے لیے سستے، اور آپ کو re-trigger کیے بغیر data source تبدیل کرنے دیتا ہے۔
2. ہر external call پر idempotency keys
Task trigger پر idempotencyKey کو اپنے vendor APIs (Stripe، OpenAI، وغیرہ) پر idempotency keys کے ساتھ ملائیں۔ Retries end-to-end محفوظ ہوں گے۔
3. orchestration کے لیے triggerAndWait استعمال کریں، triggers کا Promise.all نہیں
Parent جو triggerAndWait call کرتا ہے پائیدار طور پر child tasks compose کرتا ہے۔ Parent جو trigger کرتا ہے اور فوری طور پر resolve کرتا ہے chain کی observability کھو دیتا ہے۔
4. runs کو tag کریں
Triggers میں tags شامل کریں (tags: ["user:123", "feature:onboarding"]) تاکہ آپ کاروباری dimensions کے ذریعے dashboard اور management API کو filter کر سکیں۔
5. init کو idempotent رکھیں
یہ ہر cold start پر چلتا ہے۔ وہاں migrations یا one-shot side effects سے بچیں۔
نتیجہ
Trigger.dev ان کام کی categories کو ہٹاتا ہے جنہیں پہلے scratch سے ایک job system بنانے کی ضرورت ہوتی تھی۔ آپ async TypeScript لکھتے ہیں، اسے کہیں سے بھی call کرتے ہیں، اور platform آپ کو out of the box پائیدار execution، scheduling، queues، retries، realtime updates اور human-in-the-loop patterns دیتا ہے۔
وہی surface جو رات کا cron چلاتا ہے وہی surface ہے جو ایک multi-step AI agent چلاتا ہے جو frontend پر stream کرتا ہے اور review کے لیے pause ہوتا ہے۔ یہ یکجائی ہی ہے جو framework کو 2026 میں ایک سنجیدہ نظر کے قابل بناتی ہے، چاہے آپ ایک SaaS چلا رہے ہوں جسے قابل اعتماد background work کی ضرورت ہے یا serverless timeout سے بچنے والی AI features بھیج رہے ہوں۔
شروعاتی Checklist:
- trigger.dev پر سائن اپ کریں یا self-hosted Docker stack چلائیں
- اپنے پراجیکٹ میں
npx trigger.dev@latest inittask({ id, run })کے ساتھ اپنا پہلا task definite کریں- اپنے API سے اسے trigger کریں اور dashboard میں run دیکھیں
- production safety کے لیے
idempotencyKeyاورconcurrencyKeyشامل کریںuseRealtimeRunکو ایک status component میں جوڑیں- CI سے
trigger.dev deploy --env prodکے ساتھ deploy کریں