বেশিরভাগ প্রোডাকশন অ্যাপ্লিকেশনের কিছু কাজের প্রয়োজন যা request/response সাইকেলে ফিট হয় না: ইমেইল পাঠানো, আপলোড প্রক্রিয়াকরণ, AI পাইপলাইন চালানো, third-party ডেটা সিঙ্ক, রিপোর্ট তৈরি। ঐতিহ্যবাহী উত্তর হল একটি কিউ (Redis, SQS, RabbitMQ), একটি ওয়ার্কার ফ্লিট, একটি শিডিউলার এবং glue কোডের একটি ভঙ্গুর স্তূপ যা প্রতিটি ডিপ্লয়ে ভেঙে যায়।
Trigger.dev সেই স্ট্যাকটিকে একটি একক TypeScript SDK-তে সংকুচিত করে। আপনি ফাংশন লেখেন, যেকোন জায়গা থেকে সেগুলিকে কল করেন এবং প্ল্যাটফর্ম কিউইং, রিট্রাই, observability, শিডিউলিং এবং টেকসই এক্সিকিউশন পরিচালনা করে। টাস্কগুলি যতক্ষণ প্রয়োজন ততক্ষণ চলে - কোন 10-সেকেন্ড সার্ভারলেস টাইমআউট নেই, রিডিপ্লয়ে কোন কাজ হারানো নেই।
কেন Trigger.dev
2026-এর পরিবর্তন হল টেকসই এক্সিকিউশন। ওয়ার্কফ্লোগুলি অবশ্যই restart, crashes, deploys এবং rate limit-এ বেঁচে থাকতে হবে। তাদের রিয়েল টাইমে UI-তে অগ্রগতি স্ট্রিম এবং মানব ইনপুটের জন্য পজ করতে হবে। Trigger.dev সংস্করণ 3-এর সাথে এই প্রয়োজনীয়তাগুলির চারপাশে পুনর্নির্মাণ করা হয়েছিল এবং তার AI অবকাঠামো সারফেস সম্প্রসারণ অব্যাহত রেখেছে।
মডেলটি সহজ: আপনি export হিসেবে টাস্কগুলি সংজ্ঞায়িত করেন, SDK সেগুলি তুলে নেয়, প্ল্যাটফর্ম সেগুলিকে আলাদা কন্টেইনারে শিডিউল এবং চালায় এবং run স্টেট সংরক্ষিত হয় যাতে আপনি resume, retry এবং observe করতে পারেন।
শুরু করা
একটি প্রজেক্ট ইনিশিয়ালাইজ করুন
npx trigger.dev@latest login npx trigger.dev@latest init
এটি একটি trigger.config.ts ফাইল এবং উদাহরণ টাস্ক সহ একটি trigger/ ডিরেক্টরি তৈরি করে। কনফিগ ফাইলটি আপনার প্রজেক্টের সত্যের উৎস: কোন ডিরেক্টরিতে টাস্ক রয়েছে, build settings, lifecycle hooks এবং রানটাইম অপশন।
// 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"], });
স্থানীয়ভাবে টাস্ক চালান
npx trigger.dev@latest dev
dev সার্ভার ক্লাউডের সাথে সংযুক্ত হয়, আপনার টাস্ক রেজিস্টার করে এবং আপনার লোকাল কোডের মাধ্যমে রান স্ট্রিম করে। আপনি আপনার এডিটরে breakpoint সেট করেন এবং সেগুলি বাস্তব ট্রিগারে আঘাত করেন - একই লুপ যা আপনি যেকোন সাধারণ Node.js প্রজেক্টে ব্যবহার করবেন।
একটি Task সংজ্ঞায়িত করা
একটি টাস্ক হল একটি export করা অবজেক্ট যার একটি অনন্য id এবং একটি run ফাংশন রয়েছে। SDK dirs-এর মধ্যে export পরিদর্শন করে এবং স্বয়ংক্রিয়ভাবে নিবন্ধন করে।
// 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 বডিতে কোন টাইমআউট নেই। প্ল্যাটফর্ম রানটাইমে নয়, কনফিগে
maxDuration-এর মাধ্যমে এক্সিকিউশন সময় পরিচালনা করে। - Throws হল retry। SDK ব্যতিক্রমগুলি ধরে এবং
retryপলিসি অনুযায়ী exponential backoff সহ পুনরায় চালায়। - রিটার্ন মান সংরক্ষিত। অন্যান্য টাস্ক এবং আপনার ফ্রন্টএন্ড যেকোন জায়গা থেকে
run.outputপড়তে পারে।
Tasks ট্রিগার করা
আপনি আপনার ব্যাকএন্ড, আপনার API রুট বা অন্য একটি টাস্ক থেকে একটি টাস্ক কল করেন।
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 - অগ্রগতি ট্র্যাক বা প্রদর্শন করতে এটি ব্যবহার করুন
অপশনগুলি একটি কলে অনেক আচরণ আনলক করে:
idempotencyKey- যদি একই কী সহ একটি run ইতিমধ্যে বিদ্যমান থাকে, SDK কাজ ডুপ্লিকেট করার পরিবর্তে বিদ্যমান হ্যান্ডেল ফেরত দেয়।concurrencyKey- কী শেয়ার করা runs serialize করে যাতে আপনি per-tenant rate limit ছাড়িয়ে না যান।queue.concurrencyLimit- সমস্ত কী জুড়ে কিউয়ের জন্য বৈশ্বিক ক্যাপ।delay- ভবিষ্যতের সময়ের জন্য run শিডিউল করে।ttl- যদি run ততক্ষণ পর্যন্ত শুরু না হয়, এটি স্বয়ংক্রিয়ভাবে মেয়াদ শেষ করে।
Batch trigger
fan-out workload-এর জন্য, batchTrigger প্রতি কলে 500 আইটেম পর্যন্ত গ্রহণ করে এবং প্রতি আইটেম একটি run তৈরি করে।
await sendWelcomeEmail.batchTrigger( newUsers.map((u) => ({ payload: { email: u.email, name: u.name }, options: { idempotencyKey: `welcome-${u.id}` }, })) );
শিডিউল করা Tasks
Cron জব প্রথম-শ্রেণীর ঘোষণা হয়ে ওঠে। schedule নিজেই একটি পৃথক অবজেক্ট যা আপনি একটি টাস্কের সাথে একাধিকবার সংযুক্ত করতে পারেন।
// 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 schedule-এর জন্য - বলুন, প্রতি গ্রাহক একটি 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 কলটিকে idempotent করে: deploy সময়ে একই কোড পুনরায় চালালে ডুপ্লিকেট schedule স্ট্যাক হয় না।
কিউ, কনকারেন্সি এবং আইডেমপটেন্সি
তিনটি প্রিমিটিভ বেশিরভাগ rate-limiting এবং ordering প্রয়োজন কভার করে।
একটি সাধারণ প্যাটার্ন: একজন vendor-এর rate limit সম্মান করতে প্রতি tenant একটি কিউ ছোট per-key concurrency সহ, প্লাস retries নিরাপদ করতে একটি idempotency কী।
await syncShopifyOrders.trigger( { shopId }, { queue: { name: `shopify-${shopId}`, concurrencyLimit: 2 }, concurrencyKey: shopId, idempotencyKey: `sync-${shopId}-${Date.now() / 60_000 | 0}`, } );
অপেক্ষা এবং দীর্ঘ-চলমান কাজ
টাস্কগুলি একটি সংযোগ ধরে রাখা বা compute পোড়ানো ছাড়াই pause করতে পারে। প্ল্যাটফর্ম স্টেট সংরক্ষণ করে এবং অপেক্ষা সম্পূর্ণ হলে ফাংশন resume করে।
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 ট্রিগার করে এবং child সম্পূর্ণ হওয়া পর্যন্ত parent suspend করে। আপনি async ফাংশনের মতো task compose করেন, কিন্তু orchestration দিন বা সপ্তাহ ধরে টেকসইভাবে চলে।
wait.forToken সহ Human-in-the-loop
approval flows এবং AI gates-এর জন্য, wait.forToken pause করে যতক্ষণ না আপনার অ্যাপ্লিকেশন একটি ফলাফল সহ 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); }, });
এডিটর একটি UI খোলেন, খসড়া পর্যালোচনা করেন, Approve ক্লিক করেন এবং আপনার ব্যাকএন্ড টোকেন সম্পূর্ণ করে। টাস্কটি যেখানে ছেড়েছিল সেখান থেকে চলে - এমনকি ঘন্টা বা দিন কেটে গেলেও।
Lifecycle Hooks
আপনি একটি টাস্ক বা বিশ্বব্যাপী trigger.config.ts-এ init, onStart, onSuccess এবং onFailure সংযুক্ত করতে পারেন। 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 নয়, তাই এটি client এবং pool সেট আপ করার সঠিক জায়গা।
ফ্রন্টএন্ডে রিয়েলটাইম
Trigger.dev একটি streaming API-এর মাধ্যমে run state পরিবর্তন - 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 তৈরি করেন এবং ক্লায়েন্টে পাঠান। হুক 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 এজেন্ট এবং স্ট্রিমিং
Trigger.dev AI এজেন্টদের জন্য একটি জনপ্রিয় runtime হয়ে উঠেছে কারণ একই প্রিমিটিভ - টেকসই এক্সিকিউশন, retries, waits, real-time metadata, human-in-the-loop - ঠিক যা এজেন্টদের প্রয়োজন। আপনি run চলাকালীন একটি model provider থেকে token-গুলিকে 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 থেকে বেঁচে থাকে।
ডিপ্লয়িং
ডিপ্লয় আপনার টাস্ক একটি versioned bundle-এ কম্পাইল করে, একটি container তৈরি করে এবং পারমাণবিকভাবে traffic swap করে। পুরানো in-flight runs পূর্ববর্তী version ব্যবহার চালিয়ে যায়।
npx trigger.dev@latest deploy --env prod
CI-তে আপনি সাধারণত এটিকে একই workflow-এ যুক্ত করেন যা আপনার অ্যাপ পাঠায়:
# .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 পরিচালনা করে তা mirror করে।
Self-Hosting বনাম Cloud
Trigger.dev Apache 2.0 লাইসেন্সের অধীনে 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 যা প্রদান করে |
| সর্বোত্তম জন্য | বেশিরভাগ team | কঠোর data residency, custom infra |
SDK এবং CLI mode-গুলির মধ্যে অভিন্ন - আপনি একটি profile flag পরিবর্তন করেন এবং আপনার নিজস্ব instance-এ point করেন।
Best Practices
1. payloads ছোট এবং serializable রাখুন
ID এবং reference পাস করুন, সম্পূর্ণ object নয়। 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 নয়
triggerAndWait কল করা একটি parent টেকসইভাবে child tasks compose করে। trigger করে এবং অবিলম্বে resolve করা parent chain-এর observability হারায়।
4. runs ট্যাগ করুন
triggers-এ tags যোগ করুন (tags: ["user:123", "feature:onboarding"]) যাতে আপনি ব্যবসায়িক dimension দ্বারা dashboard এবং management API filter করতে পারেন।
5. init idempotent রাখুন
এটি প্রতি cold start-এ চলে। সেখানে migrations বা one-shot side effect এড়ান।
উপসংহার
Trigger.dev কাজের সেই বিভাগগুলি সরিয়ে দেয় যেগুলির জন্য একসময় scratch থেকে একটি job system তৈরি করতে হত। আপনি async TypeScript লেখেন, যেকোন জায়গা থেকে কল করেন এবং প্ল্যাটফর্ম আপনাকে box-এর বাইরে টেকসই execution, scheduling, queues, retries, real-time updates এবং human-in-the-loop pattern দেয়।
একই surface যা একটি রাতের cron-কে শক্তি দেয় তা সেই surface যা frontend-এ stream করা এবং review-এর জন্য pause করা একটি multi-step AI agent-কে শক্তি দেয়। সেই অভিসৃতিই framework-কে 2026-এ একটি গুরুতর দৃষ্টিভঙ্গির যোগ্য করে তোলে, আপনি একটি SaaS পরিচালনা করছেন কিনা যার নির্ভরযোগ্য background work প্রয়োজন বা serverless timeout-এর বাইরে বেঁচে থাকা AI features পাঠাচ্ছেন।
শুরু করার চেকলিস্ট:
- trigger.dev-এ সাইন আপ করুন বা self-hosted Docker stack চালান
- আপনার প্রজেক্টে
npx trigger.dev@latest inittask({ id, run })দিয়ে আপনার প্রথম task সংজ্ঞায়িত করুন- আপনার API থেকে এটি trigger করুন এবং dashboard-এ run দেখুন
- production নিরাপত্তার জন্য
idempotencyKeyএবংconcurrencyKeyযোগ করুনuseRealtimeRunএকটি status component-এ যুক্ত করুন- CI থেকে
trigger.dev deploy --env prodদিয়ে deploy করুন