spinny:~/writing $ less trigger-dev-background-jobs-guide.md
12Cogu 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.34[Trigger.dev](https://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.56## Neden Trigger.dev782026'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.910```mermaid11graph LR12 App[Uygulamaniz] -->|trigger| API[Trigger.dev API]13 API --> Queue[Dayanikli Queue]14 Queue --> Worker[Worker Container]15 Worker -->|run task| Task[Task Kodunuz]16 Task -->|metadata| Realtime[Realtime Stream]17 Realtime --> UI[React UI]18 Worker --> Storage[Run Durum Store]19```2021Model 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.2223## Baslarken2425### Bir proje baslatin2627```bash28npx trigger.dev@latest login29npx trigger.dev@latest init30```3132Bu, 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.3334```typescript35// trigger.config.ts36import { defineConfig } from "@trigger.dev/sdk";3738export default defineConfig({39 project: "proj_abc123",40 runtime: "node",41 logLevel: "log",42 maxDuration: 3600,43 retries: {44 enabledInDev: true,45 default: {46 maxAttempts: 3,47 factor: 2,48 minTimeoutInMs: 1000,49 maxTimeoutInMs: 30_000,50 },51 },52 dirs: ["./trigger"],53});54```5556### Tasks'lari yerel olarak calistirin5758```bash59npx trigger.dev@latest dev60```6162dev 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.6364## Bir Task Tanimlamak6566Bir task, benzersiz bir `id` ve bir `run` fonksiyonu ile dista aktarilan bir nesnedir. SDK, `dirs` boyunca dista aktarmalari inceler ve otomatik olarak kaydeder.6768```typescript69// trigger/send-welcome-email.ts70import { task } from "@trigger.dev/sdk";71import { Resend } from "resend";7273const resend = new Resend(process.env.RESEND_API_KEY);7475export const sendWelcomeEmail = task({76 id: "send-welcome-email",77 retry: {78 maxAttempts: 5,79 factor: 1.8,80 minTimeoutInMs: 500,81 maxTimeoutInMs: 30_000,82 },83 run: async (payload: { email: string; name: string }) => {84 const { data, error } = await resend.emails.send({85 from: "hello@spinny.dev",86 to: payload.email,87 subject: `Welcome, ${payload.name}`,88 html: `<p>Glad you are here, ${payload.name}.</p>`,89 });9091 if (error) throw error;92 return { messageId: data?.id };93 },94});95```9697Dikkat edilecek uc sey:98991. **Run govdesinde timeout yok.** Platform yurutme suresini runtime'da degil, config'deki `maxDuration` araciligiyla yonetir.1002. **Throws retry'dir.** SDK istisnalari yakalar ve `retry` politikasina gore exponential backoff ile yeniden calistirir.1013. **Donus degeri kalici olarak korunur.** Diger tasks ve frontend'iniz herhangi bir yerden `run.output` okuyabilir.102103## Tasks'lari Tetikleme104105Bir task'i backend'inizden, API rotalarinizdan veya baska bir task'tan cagirirsiniz.106107```typescript108import { sendWelcomeEmail } from "@/trigger/send-welcome-email";109110const handle = await sendWelcomeEmail.trigger(111 { email: "user@example.com", name: "Alex" },112 {113 idempotencyKey: `welcome-${userId}`,114 concurrencyKey: `tenant-${tenantId}`,115 queue: { name: "emails", concurrencyLimit: 50 },116 delay: "30s",117 ttl: "10m",118 }119);120121console.log(handle.id); // run_xyz - ilerlemeyi izlemek veya goruntulemek icin kullanin122```123124Secenekler tek bir cagrida cok fazla davranis acar:125126- **`idempotencyKey`** - ayni anahtara sahip bir run zaten varsa, SDK isi cogaltmak yerine mevcut hosgeldiniz dondurur.127- **`concurrencyKey`** - anahtar paylasan runs'lari serilestirir, boylece tenant basi rate limit asilmaz.128- **`queue.concurrencyLimit`** - tum anahtarlar boyunca queue icin global cap.129- **`delay`** - run'i gelecek bir zaman icin planlar.130- **`ttl`** - run o zamana kadar baslamamissa, otomatik olarak suresini doldurur.131132### Batch trigger133134Fan-out yukleri icin, `batchTrigger` cagri basina 500 ogeye kadar kabul eder ve oge basina bir run olusturur.135136```typescript137await sendWelcomeEmail.batchTrigger(138 newUsers.map((u) => ({139 payload: { email: u.email, name: u.name },140 options: { idempotencyKey: `welcome-${u.id}` },141 }))142);143```144145## Planlanmis Tasks146147Cron isleri birinci sinif bildirimler haline gelir. Schedule'in kendisi, bir task'a birden cok kez ekleyebileceginiz ayri bir nesnedir.148149```typescript150// trigger/daily-digest.ts151import { schedules } from "@trigger.dev/sdk";152153export const dailyDigest = schedules.task({154 id: "daily-digest",155 cron: "0 9 * * *",156 run: async (payload) => {157 console.log("Scheduled at:", payload.timestamp);158 console.log("Last run:", payload.lastTimestamp);159 console.log("Timezone:", payload.timezone);160 console.log("Next 5 runs:", payload.upcoming);161162 await sendDigestForDate(payload.timestamp);163 },164});165```166167Tenant basi schedules icin - diyelim ki, musteri basi bir cron - bunlari management API uzerinden dinamik olarak olusturursunuz.168169```typescript170import { schedules } from "@trigger.dev/sdk";171172await schedules.create({173 task: "daily-digest",174 cron: "0 9 * * *",175 timezone: "America/New_York",176 externalId: `customer_${customerId}`,177 deduplicationKey: `digest-${customerId}`,178});179```180181`deduplicationKey` cagriyi idempotent yapar: deploy zamanin ayni kodu yeniden calistirmak yinelenen schedules yigmaz.182183## Queues, Concurrency ve Idempotency184185Uc primitive cogu rate-limiting ve siralama ihtiyacini karsilar.186187```mermaid188graph TB189 Trigger[trigger payload] --> IK{idempotencyKey<br/>gorulen?}190 IK -->|evet| Reuse[Mevcut run'u dondur]191 IK -->|hayir| CK[concurrencyKey kova]192 CK --> Q[concurrencyLimit ile<br/>Queue]193 Q -->|slot mevcut| Run[task'i calistir]194 Q -->|slotlar dolu| Wait[Queue'da bekle]195```196197Yaygin 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.198199```typescript200await syncShopifyOrders.trigger(201 { shopId },202 {203 queue: { name: `shopify-${shopId}`, concurrencyLimit: 2 },204 concurrencyKey: shopId,205 idempotencyKey: `sync-${shopId}-${Date.now() / 60_000 | 0}`,206 }207);208```209210## Waits ve Uzun Suren Is211212Tasks bir baglanti tutmadan veya compute yakmadan duraklatilabilir. Platform durumu kalici olarak korur ve wait tamamlandiginda fonksiyonu devam ettirir.213214```typescript215import { wait } from "@trigger.dev/sdk";216217export const onboarding = task({218 id: "onboarding",219 run: async (payload: { userId: string }) => {220 await sendWelcomeEmail.triggerAndWait({ userId: payload.userId });221 await wait.for({ days: 1 });222 await sendTipsEmail.trigger({ userId: payload.userId });223 await wait.until({ date: oneWeekFromSignup(payload.userId) });224 await sendUpgradeOffer.trigger({ userId: payload.userId });225 },226});227```228229`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.230231### `wait.forToken` ile Human-in-the-loop232233Onay akislari ve AI gates icin, `wait.forToken` uygulamaniz bir sonucla geri arayana kadar duraklatir.234235```typescript236import { task, wait } from "@trigger.dev/sdk";237238export const publishPost = task({239 id: "publish-post",240 run: async (payload: { draftId: string }) => {241 const draft = await generateAIContent(payload.draftId);242243 const token = await wait.createToken({ timeout: "7d" });244 await notifyEditor({ draftId: draft.id, token: token.id });245246 const decision = await wait.forToken<{ approved: boolean; notes?: string }>(247 token.id248 );249250 if (decision.approved) {251 return await publish(draft);252 }253 return await applyFeedback(draft, decision.notes);254 },255});256```257258Editor 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.259260## Lifecycle Hooks261262`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.263264```typescript265// trigger.config.ts266export default defineConfig({267 // ...268 init: async () => {269 Sentry.init({ dsn: process.env.SENTRY_DSN });270 },271 onFailure: async ({ error, ctx }) => {272 Sentry.captureException(error, {273 tags: { taskId: ctx.task.id, runId: ctx.run.id },274 });275 },276});277```278279`init` boot'ta worker container basina bir kez calisir, run basina degil, bu yuzden istemcileri ve havuzlari ayarlamak icin dogru yerdir.280281## Frontend'de Realtime282283Trigger.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.284285```typescript286// trigger/process-video.ts287import { task, metadata } from "@trigger.dev/sdk";288289export const processVideo = task({290 id: "process-video",291 run: async (payload: { videoId: string }) => {292 metadata.set("stage", "transcoding");293 await transcode(payload.videoId);294295 metadata.set("stage", "thumbnails");296 await generateThumbnails(payload.videoId);297298 metadata.set("stage", "uploading");299 const url = await uploadToCDN(payload.videoId);300301 return { url };302 },303});304```305306```tsx307// components/VideoStatus.tsx308"use client";309import { useRealtimeRun } from "@trigger.dev/react-hooks";310import type { processVideo } from "@/trigger/process-video";311312export function VideoStatus({313 runId,314 publicAccessToken,315}: {316 runId: string;317 publicAccessToken: string;318}) {319 const { run, error } = useRealtimeRun<typeof processVideo>(runId, {320 accessToken: publicAccessToken,321 });322323 if (error) return <p>Error: {error.message}</p>;324 if (!run) return <p>Loading...</p>;325326 return (327 <div>328 <p>Status: {run.status}</p>329 <p>Stage: {String(run.metadata?.stage ?? "queued")}</p>330 {run.output?.url && <video src={run.output.url} controls />}331 </div>332 );333}334```335336Belirli bir run'a kapsam belirlenmis sunucu tarafinda public access token uretirsiniz ve istemciye gondersiniz. Hook auth, yeniden baglanmayi ve artisli guncellemeleri yonetir.337338Tek seferde trigger-and-subscribe icin:339340```tsx341import { useRealtimeTaskTrigger } from "@trigger.dev/react-hooks";342343const { submit, run, isLoading } = useRealtimeTaskTrigger<typeof processVideo>(344 "process-video",345 { accessToken: publicAccessToken }346);347348<button onClick={() => submit({ videoId })} disabled={isLoading}>349 Process video350</button>;351```352353## AI Agents ve Streaming354355Trigger.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.356357```typescript358import { task, metadata } from "@trigger.dev/sdk";359import { streamText } from "ai";360import { anthropic } from "@ai-sdk/anthropic";361362export const researchAgent = task({363 id: "research-agent",364 maxDuration: 1800,365 run: async (payload: { question: string }) => {366 const result = streamText({367 model: anthropic("claude-opus-4-7"),368 system: "You are a research assistant. Use the web.",369 prompt: payload.question,370 tools: { webSearch },371 });372373 let fullText = "";374 for await (const chunk of result.textStream) {375 fullText += chunk;376 metadata.set("partial", fullText);377 }378379 return { answer: fullText, usage: await result.usage };380 },381});382```383384Frontend `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.385386## Deploying387388Deploy'lar tasks'larinizi versiyonlu bir bundle'a derler, bir container olusturur ve trafigi atomik olarak degistirir. Eski ucustaki runs onceki versiyonu kullanmaya devam eder.389390```bash391npx trigger.dev@latest deploy --env prod392```393394CI'da bunu genellikle uygulamanizi gonderen ayni workflow'a baglarsiniz:395396```yaml397# .github/workflows/deploy.yml398- name: Deploy Trigger.dev399 env:400 TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}401 run: npx trigger.dev@latest deploy --env prod402```403404Onizleme 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.405406## Self-Hosting vs Cloud407408Trigger.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.409410| Yon | Cloud | Self-hosted |411|--------|-------|-------------|412| **Setup** | Kayit ol, `init` calistir | docker-compose veya Helm chart calistir |413| **Olcekleme** | Otomatik | Sizin sorumlulugunuz |414| **Pricing** | Run basi + compute basi | Sadece infra maliyeti |415| **Compliance** | SOC 2 | Ortaminizin sagladigi her sey |416| **En iyisi** | Cogu takim | Sıkı veri ikamet, ozel infra |417418SDK ve CLI modlar arasinda aynidir - bir profile flag'i degistirirsiniz ve kendi instance'iniza yonlendirirsiniz.419420## En Iyi Uygulamalar421422### 1. Payloads'lari kucuk ve serilestirilebilir tutun423424ID'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.425426### 2. Her dis cagride idempotency keys427428Task tetikleyicisindeki `idempotencyKey`'i vendor API'larinizdaki (Stripe, OpenAI, vb.) idempotency keys ile birlestirin. Retries uctan uca guvenli olacaktir.429430### 3. Orkestrasyon icin `triggerAndWait` kullanin, tetikleyicilerin `Promise.all`'i degil431432`triggerAndWait` cagiran bir parent dayanikli olarak cocuk tasks'lari olusturur. Tetikleyen ve hemen cozen bir parent zincirin gozlemlenebilirligini kaybeder.433434### 4. Runs'lari etiketleyin435436Tetikleyicilere `tags` ekleyin (`tags: ["user:123", "feature:onboarding"]`) boylece dashboard ve management API'yi is boyutlarina gore filtreleyebilirsiniz.437438### 5. `init`'i idempotent tutun439440Her cold start'ta calisir. Orada migrations veya tek seferlik yan etkilerden kacinin.441442## Sonuc443444Trigger.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.445446Bir 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.447448> **Baslama Kontrol Listesi:**449>450> - [x] trigger.dev'de kayit olun veya self-hosted Docker stack'i calistirin451> - [x] Projenizde `npx trigger.dev@latest init`452> - [x] `task({ id, run })` ile ilk task'inizi tanimlayin453> - [x] Bunu API'nizden tetikleyin ve dashboard'da run'i izleyin454> - [x] Production guvenligi icin `idempotencyKey` ve `concurrencyKey` ekleyin455> - [x] `useRealtimeRun`'i bir status component'ine baglayin456> - [x] CI'dan `trigger.dev deploy --env prod` ile deploy edin457
:Trigger.dev: TypeScript ile Dayanikli Arka Plan Islerini ve AI Iş Akislarilines 1-457 (END) — press q to close