Web razvoj
Next.jsVercelPozadinski posloviRedoviCronRedisUpstashArhitekturaDevOps

Next.js pozadinski poslovi u 2026.: redovi, Cron i dugotrajni zadaci na Vercelu (i šire)

AO
Adrijan Omićević
·14 min čitanja

# Što ćeš naučiti#

Next.js je odličan za request-response workloadove, ali pozadinski rad je mjesto gdje mnogi timovi gube vrijeme i pouzdanost. U 2026., pozadinski poslovi “production-grade” razine u Next.js-u najčešće kombiniraju tri građevna bloka: zakazane okidače, trajni (durable) red i workera koji može raditi dovoljno dugo da završi posao.

Ovaj vodič pokriva praktične obrasce za pozadinski rad na Vercelu i šire: Vercel Cron, ograničenja serverlessa, redove temeljene na Redisu i Upstashu te namjenske worker servise. Dobit ćeš i kriterije odlučivanja, arhitekturne dijagrame i checklistu spremnosti za produkciju koju možeš koristiti prije puštanja.

# Stvarnost u 2026.: serverless i Next.js nisu job runner#

Next.js pokreće API rute i Server Actions kao compute vezan uz zahtjev. To je važno jer su pozadinski poslovi često suprotnost: dugotrajni su, traže retryje i osjetljivi su na throughput.

Evo ograničenja oko kojih trebaš dizajnirati:

ConcernŠto se događa na serverlessuZašto je važno za poslove
Max execution timeTvrdi timeout (ovisno o planu i platformi)Dugi zadaci se prekidaju usred rada ako ih ne “chunk-aš”
Cold startsSkokovita latencijaCron i queue consumeri mogu krenuti sporo
Nema zajamčenog in-process stateaInstance su efemerneIn-memory redovi, lockovi i praćenje napretka će pucati
Skaliranje konkurentnostiAuto-skalira s prometomSuper za burstove, ali može preopteretiti downstream sustave
Background threadoviNisu pouzdaniNe bi trebao pokretati posao nakon slanja odgovora

Ako si na Vercelu, pretpostavi da se funkcija može zaustaviti u bilo kojem trenutku i dizajniraj za idempotentnost i nastavak (resume). Ako dodatno podešavaš caching i rendering, uskladi pozadinski rad sa strategijom svježine podataka. Puno timova zaboravi tu poveznicu i završi sa zastarjelim stranicama ili nepotrebnim recomputeom. Za dublju perspektivu cachea, vidi Next.js strategije cacheiranja za SSR, ISR i SWR.

# Odabir pravog obrasca: matrica odlučivanja#

Koristi ovu matricu za brz odabir pristupa. Većina produkcijskih sustava na kraju koristi barem dva obrasca: cron plus red, i red plus workere.

Use casePreporučeni obrazacTipični alatiNapomene
Osvježavanje analitike jednom na satCron okida enqueue poslovaVercel Cron plus Upstash RedisIzbjegavaj odraditi posao unutar cron funkcije
Slanje transakcijskih emailovaRed s retryjimaUpstash Redis, BullMQ na RedisuOsiguraj idempotentnost kako bi izbjegao duplikate
Generiranje PDF-ova ili videaNamjenski worker servisContaineri na Fly.io, Render, AWS ili self-hostedNapredak u DB-u, streamanje logova
Sinkronizacija podataka iz third-party API-jaCron plus red plus rate limitingVercel Cron, Redis, workerRate limit i backoff su ključni
Export pokrenut od korisnikaEnqueue na zahtjev, polling statusaRedis red, worker, DBTablica statusa posla i potpisani URL-ovi za download
Fan-out webookovaRed, batch obradaQStash, Redis StreamsSpriječi da jedan webhook blokira API

🎯 Ključna poruka: Ako zadatak može premašiti serverless time limit ili treba snažnu semantiku retryja, stavi ga u red i pokreni u workeru. Ne pokušavaj “samo ga odraditi u API ruti”.

# Obrazac 1: Cron na Vercelu — kako to napraviti ispravno#

Vercel Cron je scheduler koji pogađa URL na tvojem deploymentu prema rasporedu. Odličan je za okidanje posla, ali nije punokrvni scheduler koji jamči single execution u svim failure modeovima osim ako dodaš vlastite zaštite.

Arhitektura: Cron okidač plus red#

Text
+-------------+         +-------------------+         +------------------+
| Vercel Cron |  HTTP   | Next.js API Route |  enqueue| Queue (Redis)    |
| (schedule)  +-------->+ /api/cron/*       +-------->+ Upstash / Redis  |
+-------------+         +-------------------+         +---------+--------+
                                                                  |
                                                                  | consume
                                                                  v
                                                         +--------+--------+
                                                         | Worker Service  |
                                                         | (long running)  |
                                                         +-----------------+

Ova arhitektura razdvaja zakazivanje od izvršavanja. Tvoja cron ruta treba raditi što manje: validirati auth, uzeti lock, enqueueati poslove i vratiti odgovor.

Minimalna Vercel Cron konfiguracija#

U Vercelu cron poslove definiraš u vercel.json. Drži rasporede jednostavnima i dovoljno učestalima da se sustav oporavi od grešaka.

JSON
{
  "crons": [
    { "path": "/api/cron/sync-products", "schedule": "0 * * * *" },
    { "path": "/api/cron/cleanup", "schedule": "15 2 * * *" }
  ]
}

Zaštiti endpoint i izbjegni duplo izvođenje#

Cron endpointi moraju biti zaštićeni. Koristi tajni header, provjeri token i dodaj distribuirani lock kako bi samo jedna egzekucija u jednom trenutku enqueueala posao.

TypeScript
// app/api/cron/sync-products/route.ts
import { NextResponse } from "next/server";
import { Redis } from "@upstash/redis";
 
export const runtime = "nodejs";
 
const redis = Redis.fromEnv();
 
export async function GET(req: Request) {
  const auth = req.headers.get("authorization");
  if (auth !== `Bearer ${process.env.CRON_SECRET}`) {
    return NextResponse.json({ error: "unauthorized" }, { status: 401 });
  }
 
  const lockKey = "lock:cron:sync-products";
  const lock = await redis.set(lockKey, "1", { nx: true, ex: 55 });
  if (!lock) return NextResponse.json({ ok: true, skipped: "locked" });
 
  // enqueue lightweight units of work
  await redis.lpush("q:product-sync", JSON.stringify({ type: "sync", at: Date.now() }));
 
  return NextResponse.json({ ok: true });
}

Ovo koristi Redis lock s istekom kraćim od intervala rasporeda. Ako cron padne, lock se sam oslobodi.

⚠️ Upozorenje: Ne zovi third-party API-je direktno unutar cron funkcije ako može trajati dulje od par sekundi ili mora paginirati kroz puno rezultata. Udarit ćeš u timeoutove, a djelomični napredak bit će teško oporaviti.

# Obrazac 2: Redovi u Next.js-u s Upstashom i Redisom#

Redovi ti daju trajnost, retryje i buffer kada promet naglo skoči. Također ti omogućuju da postaviš konkurentnost koja odgovara ograničenjima tvojih downstream sustava.

U 2026. čest setup na Vercelu je:

  • Next.js API rute i Server Actions enqueueaju poslove.
  • Upstash Redis sprema red.
  • Worker konzumira poslove na platformi koja podržava dugotrajne procese.

Model podataka za Redis red#

Neka poruka u redu bude minimalna. Velike payloadove drži u object storageu ili bazi, a u poruci proslijedi reference.

FieldPrimjerZašto
jobIdjob_01HZY...Korelacija i deduplikacija
typesend_emailRutiranje i handleri
payloadRefdb:export:123Izbjegavanje velikih poruka
attempt0Upravljanje retryjima
createdAt1714600000000Debugging i SLA-ovi

Enqueue iz Server Action ili API rute#

Koristi idempotency key za akcije koje pokreće korisnik. Praktičan pristup je spremiti idempotency key u Redis s TTL-om i preskočiti duplikate.

TypeScript
// app/actions/requestExport.ts
"use server";
 
import { Redis } from "@upstash/redis";
import { nanoid } from "nanoid";
 
const redis = Redis.fromEnv();
 
export async function requestExport(userId: string, idempotencyKey: string) {
  const dedupeKey = `dedupe:export:${userId}:${idempotencyKey}`;
  const ok = await redis.set(dedupeKey, "1", { nx: true, ex: 3600 });
  if (!ok) return { status: "duplicate" as const };
 
  const jobId = `job_${nanoid()}`;
  await redis.lpush(
    "q:exports",
    JSON.stringify({ jobId, type: "export_csv", userId, attempt: 0, createdAt: Date.now() })
  );
 
  return { status: "queued" as const, jobId };
}

Ovo sprječava da slučajni double-click napravi više exportova.

Konzumiranje poslova: worker petlja s visibility timeoutom#

Naivan RPOP može izgubiti poslove ako se worker sruši usred obrade. Preferiraj pouzdan obrazac poput BRPOPLPUSH ili Redis Streams. Ako želiš jednostavnost, implementiraj processing listu plus “reaper”.

TypeScript
// worker/consumeExports.ts
import { Redis } from "@upstash/redis";
 
const redis = Redis.fromEnv();
 
async function main() {
  while (true) {
    const job = await redis.brpoplpush("q:exports", "q:exports:processing", 10);
    if (!job) continue;
 
    try {
      const msg = JSON.parse(job);
      // do work...
      await redis.lrem("q:exports:processing", 1, job);
    } catch (e) {
      // keep in processing for a reaper to retry, or move to DLQ
      console.error("job_failed", { error: String(e) });
    }
  }
}
 
main().catch((e) => {
  console.error(e);
  process.exit(1);
});

Zatim dodaš periodični reaper job koji provjerava q:exports:processing i vraća u red zastarjele stavke na temelju timestampova spremljenih u zasebnom hashu. Ako ne želiš održavati ovu semantiku, razmisli o queue biblioteci višeg nivoa ili Redis Streams consumer grupama.

💡 Savjet: Kreni s jednim redom po domeni workloada, ne jednim redom po tipu posla. Primjer: q:emails, q:exports, q:sync. Ovo drži operacije pod kontrolom i omogućuje postavljanje konkurentnosti po domeni.

# Obrazac 3: Managed HTTP redovi za “serverless first” timove#

Ako tvoj tim želi izbjeći pokretanje persistent workera, managed HTTP-based isporuka može biti dobar kompromis. Tipični tok je:

  • Tvoja aplikacija objavi poruku na endpoint reda.
  • Queue servis retryja isporuku na handler URL dok ne uspije.
  • Tvoj handler mora biti idempotentan jer može primiti duplikate.

Ovo radi dobro za zadatke koji se mogu završiti unutar serverless time limitova, ali i dalje trebaju retryje i buffering, poput fan-out webookova, slanja emailova ili kratkih transformacija podataka.

Arhitektura: HTTP isporuka iz reda#

Text
+------------------+         publish         +--------------------+
| Next.js (Vercel) |------------------------>+ Managed HTTP Queue  |
| API / Actions    |                         | retries + backoff   |
+--------+---------+                         +---------+----------+
         ^                                             |
         |                                             | deliver HTTP
         |                                             v
         |                                    +--------+---------+
         |                                    | Next.js Handler   |
         |                                    | /api/jobs/*       |
         |                                    +------------------+

Tradeoff je jednostavan: lakše operacije, manje kontrole nad throughputom i konkurentnošću u odnosu na namjenski worker na Redisu.

# Obrazac 4: Dugotrajni zadaci s namjenskim workerima#

Sve što je CPU-heavy ili time-heavy ne bi smjelo ići na serverless. Česti primjeri:

  • Generiranje PDF-a s headless Chromiumom
  • Transkodiranje videa
  • Veliki data exportovi
  • Višekoračni ETL pipelineovi

Gdje pokretati workere u 2026.#

Odaberi platformu za workere koja odgovara tvojoj operativnoj zrelosti i compliance potrebama.

PlatformNajbolje zaPrednostiNedostaci
Fly.io ili RenderMali do srednji timoviJednostavan deploy, dobar DXRegionalno podešavanje i scaling odluke
AWS ECS ili KubernetesVeće organizacijePotpuna kontrola, visok scaleViše ops overhead-a
VM sa systemdOsjetljivo na trošakNajjeftiniji predvidivi computeRučno skaliranje i monitoring
n8n (automatizacija)Business workflowoviBrza iteracija, puno konektoraNije idealno za CPU-heavy workloadove

Ako je tvoj workload uglavnom integracije i odobravanja, razmisli o prebacivanju dijelova u automatizaciju. Često kombiniramo Next.js za product UI i n8n za back-office workflowove poput CRM sinkronizacije, generiranja računa i rutiranja upozorenja. Vidi Samioda automation.

Praćenje statusa posla u bazi#

Uvijek trajno spremaj stanje posla izvan workera kako bi UI mogao prikazati napredak i kako bi se moglo nastaviti nakon restarta.

StateZnačenjeSpremljena polja
queuedprihvaćeno, čekajobId, type, createdAt
runningobrada krenulastartedAt, workerId
succeededzavršenofinishedAt, resultRef
failedpremašeni retryji ili fatalnofinishedAt, errorCode, errorMessage

Tok user exporta postaje:

  1. 1
    UI zatraži export, server enqueuea posao i upiše queued.
  2. 2
    Worker postavi running, odradi obradu, spremi datoteku u object storage.
  3. 3
    Worker postavi succeeded s referencom na potpisani download URL.
  4. 4
    UI poll-a status posla ili koristi WebSocket evente ako ih imaš.

# Praktičan end-to-end primjer: sinkronizacija proizvoda uz rate limit#

Pretpostavimo da moraš sinkronizirati 200.000 proizvoda iz third-party API-ja s rate limitom 60 zahtjeva u minuti. Ako to pokušaš u jednom cron pozivu, failat će, a ako to radiš na svaki request, preopteretit ćeš API.

Robustan pristup:

  • Vercel Cron se pokreće svaki sat.
  • Cron enqueuea page poslove za workera.
  • Worker obrađuje stranice s konkurentnošću 1 do 3, poštujući limite.
  • Worker zapisuje napredak u DB, kako bi se moglo nastaviti.

Primjer: Cron enqueuea page poslove#

TypeScript
// app/api/cron/enqueue-product-pages/route.ts
import { NextResponse } from "next/server";
import { Redis } from "@upstash/redis";
 
export const runtime = "nodejs";
const redis = Redis.fromEnv();
 
export async function GET(req: Request) {
  if (req.headers.get("authorization") !== `Bearer ${process.env.CRON_SECRET}`) {
    return NextResponse.json({ error: "unauthorized" }, { status: 401 });
  }
 
  // Example: 100 pages, keep messages small
  for (let page = 1; page <= 100; page++) {
    await redis.lpush("q:sync-products", JSON.stringify({ type: "sync_page", page, attempt: 0 }));
  }
 
  return NextResponse.json({ ok: true, enqueued: 100 });
}

Worker primjenjuje backoff i retryje#

Neka retry logika bude deterministička. Koristi eksponencijalni backoff poput delayMs = min(60000, 1000 * 2^attempt) i prebaci u dead-letter queue nakon N pokušaja.

TypeScript
// worker/syncProducts.ts
import { Redis } from "@upstash/redis";
 
const redis = Redis.fromEnv();
 
function backoffMs(attempt: number) {
  return Math.min(60000, 1000 * Math.pow(2, attempt));
}
 
async function sleep(ms: number) {
  await new Promise((r) => setTimeout(r, ms));
}
 
async function main() {
  while (true) {
    const raw = await redis.brpoplpush("q:sync-products", "q:sync-products:processing", 10);
    if (!raw) continue;
 
    const msg = JSON.parse(raw) as { page: number; attempt: number };
 
    try {
      // Call third-party API, write to DB, etc.
      // Respect rate limit with small delays per page
      await sleep(1200);
 
      await redis.lrem("q:sync-products:processing", 1, raw);
    } catch (e) {
      const attempt = msg.attempt + 1;
      await redis.lrem("q:sync-products:processing", 1, raw);
 
      if (attempt >= 5) {
        await redis.lpush("q:sync-products:dlq", JSON.stringify({ ...msg, attempt, error: String(e) }));
      } else {
        await sleep(backoffMs(attempt));
        await redis.lpush("q:sync-products", JSON.stringify({ ...msg, attempt }));
      }
    }
  }
}
 
main().catch((e) => {
  console.error(e);
  process.exit(1);
});

Ovo je namjerno jednostavno, ali pokazuje ključnu mehaniku: visibility listu, retryje, backoff i DLQ.

# Observability: razlika između poslova koji rade i poslova kojima možeš upravljati#

Pozadinski poslovi failaju drugačije od web zahtjeva. Bez observabilityja, greške ćeš najčešće primijetiti tek kad se kupac požali.

Instrumentiraj tri sloja:

  1. 1
    Sloj okidanja: cron pozivi i enqueue zahtjevi.
  2. 2
    Sloj reda: lag, throughput i DLQ veličina.
  3. 3
    Sloj workera: trajanje, stopa grešaka i latencija vanjskih ovisnosti.

Minimalno, logiraj:

  • jobId
  • type
  • attempt
  • correlationId za izvorni request
  • start i end timestampove

Zatim dodaj metrike:

  • percentile trajanja posla, posebno p95 i p99
  • stopa failova po tipu posla
  • dubina reda i time-in-queue
  • broj retryja i rast DLQ-a

Ako ti treba strukturiran pristup, koristi našu checklistu i obrasce za observability u Observability web aplikacija: logging, metrike, tracing.

ℹ️ Napomena: U serverless okruženjima logovi mogu biti fragmentirani kroz invokacije. Trajno spremaj prijelaze stanja posla u tablicu u bazi kako bi mogao rekonstruirati što se dogodilo čak i ako se logovi samplaju ili rotiraju.

# Checklista spremnosti za produkciju#

Koristi ovu checklistu prije nego što se osloniš na pozadinske poslove za kritične workflowove poput naplate, notifikacija ili sinkronizacije podataka.

Sigurnost i ispravnost#

  • Idempotency key-evi za akcije pokrenute od korisnika i webhook handlere.
  • Strategija deduplikacije za cron i retryje.
  • Distribuirani lock za cron okidače i sve “singleton” poslove.
  • Pretpostavi at-least-once isporuku, uz siguran ponovni processing.
  • Jasna DLQ politika i operator runbook za replay.

Pouzdanost i skaliranje#

  • Ograničenja konkurentnosti po redu kako bi se poštovali downstream rate limitovi.
  • Backoff strategija: eksponencijalna s jitterom ako imaš puno workera.
  • Timeoutovi na svim outbound zahtjevima, nikad beskonačno.
  • Strategija chunkanja za velike poslove, uz resumable napredak.
  • Odvojeni redovi za različite workloadove kako bi se izolirali kvarovi.

Sigurnost i usklađenost (compliance)#

  • Cron i job endpointi zahtijevaju tajni token ili verifikaciju potpisa.
  • Least-privilege tajne za workere, odvojene od web aplikacije kad je moguće.
  • Osjetljivi payloadovi u DB-u ili object storageu, ne u porukama reda.
  • Audit log za kritične poslove poput isplata ili promjena pretplate.

Operacije i observability#

  • Dashboard za dubinu reda, stopu uspješnosti i DLQ veličinu.
  • Pragovi za alerte vezani uz SLA-ove, ne šum.
  • Correlation id prenesen od web requesta do posla i workera.
  • Status posla trajno spremljen u DB, uključujući razloge faila i timestampove.

Deploy i rollback#

  • Worker i aplikacija se mogu deployati neovisno.
  • Verzije sheme poruke posla, tako da stariji workeri mogu ignorirati nova polja.
  • Backward-compatible handleri tijekom roll-outa.
  • Feature flagovi za nove tipove poslova.

# Ključne poruke#

  • Koristi Vercel Cron kao okidač, ne kao engine za izvršavanje: validiraj auth, uzmi lock, enqueueaj posao, brzo vrati odgovor.
  • Pretpostavi da se serverless invokacije mogu završiti ranije i da se mogu izvršiti više puta; dizajniraj poslove da budu idempotentni i resumable.
  • Za pouzdanu obradu u pozadini kombiniraj trajni red s workerom koji podržava dugotrajni compute i kontroliranu konkurentnost.
  • Poruke u redu drži malima, a velike payloadove referenciraj iz baze ili object storagea kako bi smanjio greške i troškove.
  • Učini poslove operabilnima: spremaj stanja posla, prati lag reda i rast DLQ-a te dodaj alerte vezane uz stvarni utjecaj.

# Zaključak#

Next.js u 2026. može podržati ozbiljnu obradu u pozadini, ali samo ako prestaneš tretirati API rute kao job runner. Pouzdan put je konzistentan: cron okidači enqueueaju, redovi bufferaju i retryjaju, a workeri obrađuju uz jasna ograničenja i observability.

Ako želiš pomoć oko dizajna queue-and-worker arhitekture za Vercel, odabira između Upstasha i managed isporuke ili spajanja end-to-end observabilityja, Samioda to može implementirati s Reactom i Next.js-om, uz automatizaciju gdje ima smisla. Kreni ovdje: Samioda automatizacija, i usporedo pregledaj svoju strategiju cacheiranja uz svježinu jobova u Next.js strategije cacheiranja.

FAQ

Share
A
Adrijan OmićevićSamioda Team
All articles →

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.