Cogu production uygulamasi istek/yanit dongusune sigmayan ise ihtiyac duyar: e-posta gondermek, yuklemeleri islemek, AI pipeline'larini calistirmak, ucuncu taraf verileri senkronize etmek, raporlar uretmek. Geleneksel cevap bir kuyruktur (Redis, SQS, RabbitMQ), bir worker filosu, bir scheduler ve her deploy'da kirilan kirilgan bir glue kod yiginidir.
Trigger.dev bu yigini tek bir TypeScript SDK'sina sikistirir. Fonksiyonlari yazarsiniz, herhangi bir yerden cagirirsiniz ve platform queueing, retries, observability, scheduling ve dayanikli yurutmeyi yonetir. Tasks ihtiyac duyduklari sure boyunca calisir - 10 saniyelik serverless timeout yok, redeploy'larda kaybolan is yok.
Neden Trigger.dev
2026'daki kayma dayanikli yurutme. Is akislari yeniden baslatmalardan, kazalardan, deploy'lardan ve rate limit'lerden kurtulmali. Ayni zamanda ilerlemeyi gercek zamanli olarak UI'a stream etmeli ve insan girdisi icin duraklamali. Trigger.dev surum 3 ile bu gereksinimler etrafinda yeniden insa edildi ve AI altyapi yuzeyini genisletmeye devam ediyor.
Model basit: tasks'lari export olarak tanimlarsiniz, SDK onlari alir, platform onlari izole konteynerlerde planlar ve calistirir ve run durumu kalici olarak korunur, boylece devam edebilir, yeniden deneyebilir ve gozlemleyebilirsiniz.
Baslarken
Bir proje baslatin
npx trigger.dev@latest login npx trigger.dev@latest init
Bu, bir trigger.config.ts dosyasi ve ornek tasks'larla bir trigger/ dizini olusturur. Config dosyasi projenizin gercek kaynaktir: hangi dizinler tasks icerir, build ayarlari, lifecycle hooks ve runtime secenekleri.
// 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'lari yerel olarak calistirin
npx trigger.dev@latest dev
dev server bulutla baglantilar, tasks'larinizi kaydeder ve runs'lari yerel kodunuz uzerinden stream eder. Editorunuzde breakpoint'ler ayarlarsiniz ve gercek tetikleyicilerde bunlara ulasirsiniz - herhangi bir normal Node.js projesinde kullanacaginiz ayni dongu.
Bir Task Tanimlamak
Bir task, benzersiz bir id ve bir run fonksiyonu ile dista aktarilan bir nesnedir. SDK, dirs boyunca dista aktarmalari inceler ve otomatik olarak kaydeder.
// 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 }; }, });
Dikkat edilecek uc sey:
- Run govdesinde timeout yok. Platform yurutme suresini runtime'da degil, config'deki
maxDurationaraciligiyla yonetir. - Throws retry'dir. SDK istisnalari yakalar ve
retrypolitikasina gore exponential backoff ile yeniden calistirir. - Donus degeri kalici olarak korunur. Diger tasks ve frontend'iniz herhangi bir yerden
run.outputokuyabilir.
Tasks'lari Tetikleme
Bir task'i backend'inizden, API rotalarinizdan veya baska bir task'tan cagirirsiniz.
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 - ilerlemeyi izlemek veya goruntulemek icin kullanin
Secenekler tek bir cagrida cok fazla davranis acar:
idempotencyKey- ayni anahtara sahip bir run zaten varsa, SDK isi cogaltmak yerine mevcut hosgeldiniz dondurur.concurrencyKey- anahtar paylasan runs'lari serilestirir, boylece tenant basi rate limit asilmaz.queue.concurrencyLimit- tum anahtarlar boyunca queue icin global cap.delay- run'i gelecek bir zaman icin planlar.ttl- run o zamana kadar baslamamissa, otomatik olarak suresini doldurur.
Batch trigger
Fan-out yukleri icin, batchTrigger cagri basina 500 ogeye kadar kabul eder ve oge basina bir run olusturur.
await sendWelcomeEmail.batchTrigger( newUsers.map((u) => ({ payload: { email: u.email, name: u.name }, options: { idempotencyKey: `welcome-${u.id}` }, })) );
Planlanmis Tasks
Cron isleri birinci sinif bildirimler haline gelir. Schedule'in kendisi, bir task'a birden cok kez ekleyebileceginiz ayri bir nesnedir.
// 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); }, });
Tenant basi schedules icin - diyelim ki, musteri basi bir cron - bunlari management API uzerinden dinamik olarak olusturursunuz.
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 cagriyi idempotent yapar: deploy zamanin ayni kodu yeniden calistirmak yinelenen schedules yigmaz.
Queues, Concurrency ve Idempotency
Uc primitive cogu rate-limiting ve siralama ihtiyacini karsilar.
Yaygin bir kalip: bir vendor'un rate limit'ine saygi gostermek icin tenant basi bir queue kucuk anahtar basi concurrency ile, ayrica retry'leri guvenli kilmak icin bir idempotency anahtari.
await syncShopifyOrders.trigger( { shopId }, { queue: { name: `shopify-${shopId}`, concurrencyLimit: 2 }, concurrencyKey: shopId, idempotencyKey: `sync-${shopId}-${Date.now() / 60_000 | 0}`, } );
Waits ve Uzun Suren Is
Tasks bir baglanti tutmadan veya compute yakmadan duraklatilabilir. Platform durumu kalici olarak korur ve wait tamamlandiginda fonksiyonu devam ettirir.
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 katil ozelliktir: bir cocuk task'i tetikler ve cocuk tamamlanana kadar parent'i askiya alir. Tasks'lari async fonksiyonlar gibi olusturursunuz, ancak orkestrasyon gunler veya haftalar boyunca dayanikli olarak calisir.
wait.forToken ile Human-in-the-loop
Onay akislari ve AI gates icin, wait.forToken uygulamaniz bir sonucla geri arayana kadar duraklatir.
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 bir UI acar, taslagi inceler, Onayla'ya tiklar ve backend'iniz token'i tamamlar. Task kaldigi yerden devam eder - saatler veya gunler gecmis olsa bile.
Lifecycle Hooks
init, onStart, onSuccess ve onFailure'i bir task'a veya trigger.config.ts'de global olarak ekleyebilirsiniz. Bunlari tracing, error reporting ve paylasilan setup icin kullanin.
// 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'ta worker container basina bir kez calisir, run basina degil, bu yuzden istemcileri ve havuzlari ayarlamak icin dogru yerdir.
Frontend'de Realtime
Trigger.dev run durumu degisikliklerini - status, metadata, output - bir streaming API uzerinde yayinlar. React hooks bu stream'e abone olur ve otomatik olarak yeniden render eder.
// 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> ); }
Belirli bir run'a kapsam belirlenmis sunucu tarafinda public access token uretirsiniz ve istemciye gondersiniz. Hook auth, yeniden baglanmayi ve artisli guncellemeleri yonetir.
Tek seferde trigger-and-subscribe icin:
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 ve Streaming
Trigger.dev AI agents icin populer bir runtime haline geldi cunku ayni primitives - dayanikli yurutme, retries, waits, gercek zamanli metadata, human-in-the-loop - tam olarak agents'lerin ihtiyac duydugu seydir. Run gerceklesirken bir model saglayicisindan token'lari metadata'a stream edersiniz, frontend bunlari canli olarak render eder ve run, serverless timeout yakmadan uzun suren tool calls'tan hayatta kalir.
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 kullanir ve streaming yanitini render etmek icin run.metadata.partial okur, bir chat completion'i render edeceginiz gibi - bu sadece tam bir sayfa yenileme'den sag cikar.
Deploying
Deploy'lar tasks'larinizi versiyonlu bir bundle'a derler, bir container olusturur ve trafigi atomik olarak degistirir. Eski ucustaki runs onceki versiyonu kullanmaya devam eder.
npx trigger.dev@latest deploy --env prod
CI'da bunu genellikle uygulamanizi gonderen ayni workflow'a baglarsiniz:
# .github/workflows/deploy.yml - name: Deploy Trigger.dev env: TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }} run: npx trigger.dev@latest deploy --env prod
Onizleme ortamlari icin, --env preview --branch ${{ github.head_ref }} gecirin ve Trigger.dev branch basina izole bir ortam olusturur, bu Vercel'in onizleme deploy'larini nasil ele aldigini yansitir.
Self-Hosting vs Cloud
Trigger.dev Apache 2.0 lisansi altinda acik kaynaktir. Herhangi bir container platformunda (Docker Compose, Kubernetes, Fly.io) self-host edebilir veya trigger.dev'de yonetilen bulutu kullanabilirsiniz.
| Yon | Cloud | Self-hosted |
|---|---|---|
| Setup | Kayit ol, init calistir | docker-compose veya Helm chart calistir |
| Olcekleme | Otomatik | Sizin sorumlulugunuz |
| Pricing | Run basi + compute basi | Sadece infra maliyeti |
| Compliance | SOC 2 | Ortaminizin sagladigi her sey |
| En iyisi | Cogu takim | Sıkı veri ikamet, ozel infra |
SDK ve CLI modlar arasinda aynidir - bir profile flag'i degistirirsiniz ve kendi instance'iniza yonlendirirsiniz.
En Iyi Uygulamalar
1. Payloads'lari kucuk ve serilestirilebilir tutun
ID'ler ve referanslari gecirin, tam objeler degil. Verileri task icinde cekin. Bu queue'yu kucuk tutar, payloads loglamak icin ucuz olur ve veri kaynagini yeniden tetiklemeden degistirmenize olanak tanir.
2. Her dis cagride idempotency keys
Task tetikleyicisindeki idempotencyKey'i vendor API'larinizdaki (Stripe, OpenAI, vb.) idempotency keys ile birlestirin. Retries uctan uca guvenli olacaktir.
3. Orkestrasyon icin triggerAndWait kullanin, tetikleyicilerin Promise.all'i degil
triggerAndWait cagiran bir parent dayanikli olarak cocuk tasks'lari olusturur. Tetikleyen ve hemen cozen bir parent zincirin gozlemlenebilirligini kaybeder.
4. Runs'lari etiketleyin
Tetikleyicilere tags ekleyin (tags: ["user:123", "feature:onboarding"]) boylece dashboard ve management API'yi is boyutlarina gore filtreleyebilirsiniz.
5. init'i idempotent tutun
Her cold start'ta calisir. Orada migrations veya tek seferlik yan etkilerden kacinin.
Sonuc
Trigger.dev eskiden sifirdan bir job sistemi olusturmayi gerektiren is kategorilerini ortadan kaldirir. Async TypeScript yazarsiniz, herhangi bir yerden cagirirsiniz ve platform size kutu disinda dayanikli yurutme, scheduling, queues, retries, gercek zamanli guncellemeler ve human-in-the-loop kaliplari verir.
Bir gece cron'unu calistiran ayni yuzey, frontend'e stream eden ve inceleme icin duraklatan cok asamali bir AI agent'i calistiran yuzeydir. Bu yakinsama, framework'u 2026'da ciddi bir bakisa deger kilan seydir, ister guvenilir arka plan isi gerektiren bir SaaS isletiyor olun, ister bir serverless timeout'tan sag cikan AI ozellikleri gonderiyor olun.
Baslama Kontrol Listesi:
- trigger.dev'de kayit olun veya self-hosted Docker stack'i calistirin
- Projenizde
npx trigger.dev@latest inittask({ id, run })ile ilk task'inizi tanimlayin- Bunu API'nizden tetikleyin ve dashboard'da run'i izleyin
- Production guvenligi icin
idempotencyKeyveconcurrencyKeyekleyinuseRealtimeRun'i bir status component'ine baglayin- CI'dan
trigger.dev deploy --env prodile deploy edin