import fastify from 'fastify';
const app = fastify({ logger: true });
app.post('/api/heavy-task', async (req, reply) => {
// Exemplu de task primit de la Next.js către Node server
const { payload } = req.body;
// Pasăm direct într-o coadă locală fără să blocăm răspunsul
setImmediate(() => {
processPayload(payload);
});
return reply.status(202).send({ status: 'queued' });
});
// PM2 graceful shutdown nativ
process.on('SIGINT', async () => {
await app.close();
process.exit(0);
});
app.listen({ port: 4000, host: '0.0.0.0' });Next.js e genial dacă ai nevoie de SSR și un layer subțire de tip Backend-For-Frontend pentru pagina ta de React. Totuși, văd tot mai des echipe care încearcă să bage în app/api/ absolut toată logica de business: de la procesare de fișiere mari, până la cron-uri și integrări ciudate de webhook-uri. Dacă te afli în punctul ăsta, e momentul să faci un pas în spate.
Serverless vs. Procese long-running
Next.js a fost gândit cu mentalitatea de request-response scurt, optimizat pentru serverless sau containere stateless. Când ai un webhook care trebuie să facă un upload pe S3 și apoi să genereze trei formate de thumbnail, modelul ăsta începe să scârțâie.
Am pățit-o acum vreo doi ani la un proiect cu vreo 40k de evenimente procesate pe zi. Aveam route handlers în Next.js care se blocau din cauza memoriei când doi clienți urcau simultan PDF-uri mari. Vercel ne tăia execuția la timeout-ul de 15 secunde, iar în modul standalone pe Docker, garbage collector-ul pur și simplu nu făcea față la vârful de memorie indus de overhead-ul de React/Next.
Am extras logica aia într-un server mic de Node cu Fastify și l-am aruncat pe un VPS de 15 euro gestionat cu PM2. Conexiunile la Postgres au rămas calde prin connection pool, memoria s-a stabilizat pe la 180MB (în loc de 1.2GB cât mânca containerul de Next în spike-uri), iar latența medie a scăzut cu 35%.
Când e obligatoriu să separi backend-ul
Sunt câteva scenarii clare unde Next.js nu are ce căuta:
- Background jobs și cozi: Dacă folosești BullMQ sau RabbitMQ, ai nevoie de un worker permanent care ascultă coada. Next.js nu e făcut să țină procese de fundal în viață. Dacă serverul se restartează sau rulezi serverless, ai pierdut job-ul.
- Conexiuni persistente (WebSockets / SSE): Next.js suportă teoretic WebSockets doar dacă folosești un server custom, dar pierzi optimizările native și e un coșmar la deployment.
- Pooling agresiv la baza de date: Dacă ai zeci de instanțe serverless de Next.js, fiecare își deschide conexiunea ei la PostgreSQL. Ajungi rapid la
max_connectionsfără un Proxy scump precum PgBouncer.
Trade-off-ul pe care trebuie să-l accepți
Evident, nu totul e roz când separi serviciile. Nu mai ai luxul de a partaja tipurile de TypeScript printr-un simplu import type dacă nu ai un monorepo bine pus la punct cu Turborepo. În plus, acum ai două pipeline-uri de CI/CD, trebuie să configurezi manual CORS-ul (care mereu dă bătăi de cap cuiva din echipă) și trebuie să ai grijă de procesele de pe server prin PM2 sau Docker.
Pentru un proiect mic sau un MVP, ține totul în Next.js, e mai rapid de livrat. Dar în momentul în care ai operațiuni asincrone grele, stream-uri reale de date sau logică de background, un Node.js clasic rămâne sfânt.
Voi cum ați împărțit treaba asta: țineți totul monolit în Next sau ați spart deja API-ul separat?