const fastify = require('fastify')({ logger: true });
// Un worker simplu de procesare PDF într-un serviciu separat
fastify.post('/generate-report', async (request, reply) => {
const { data } = request.body;
// Aici facem procesarea grea fără să blocăm UI-ul principal
const pdfBuffer = await heavyPdfGeneration(data);
reply
.type('application/pdf')
.send(pdfBuffer);
});
const start = async () => {
try {
await fastify.listen({ port: 3001, host: '0.0.0.0' });
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();Văd tot mai des tendința asta de a înghesui absolut orice logică de backend în pages/api sau în noile Route Handlers din Next.js. E comod, nu zic nu. Ai totul într-un singur repo, deploy-ul e la un git push distanță pe Vercel și tipurile de TypeScript se plimbă frumos între frontend și backend fără efort. Dar, după vreo 12 ani de spart tastatura prin diverse stack-uri, am învățat că 'comod' nu înseamnă mereu 'corect' pe termen lung.
Am avut anul trecut un proiect cu vreo 8.000 de utilizatori activi unde toată procesarea de imagini și generarea de rapoarte PDF fusese băgată inițial într-un endpoint de Next. La început a mers brici. Apoi, când traficul a crescut și am început să avem procesări simultane, am dat de dracu'. Serverul de UI devenea lent sau chiar indisponibil pentru că event loop-ul era blocat de procesări grele de CPU. Am luat decizia să mutăm toată logica aia într-un microserviciu separat construit cu Fastify și am economisit cam 35% la resursele de RAM, plus că UI-ul a redevenit instantaneu.
Când Next.js devine o frână reală
Dacă ai nevoie de un API intern care doar comunică între alte servicii sau care face heavy lifting, Next.js e pur și simplu prea mult balast. Framework-ul are un overhead de boot-up și de memorie pe care nu-l poți ignora dacă vrei performanță brută. Un server Node chior pornește în milisecunde și consumă o fracțiune din ce are nevoie un runtime de Next care trebuie să care după el tot mecanismul de rendering și routing complex.
Mai e și problema job-urilor de fundal. Dacă folosești ceva gen BullMQ pentru cozi de mesaje, Next.js e o alegere proastă. Framework-ul e gândit să fie stateless, mai ales dacă faci deploy pe platforme serverless. Un worker are nevoie de o conexiune persistentă la Redis și trebuie să ruleze continuu în background. Dacă îl pui în interiorul Next, te bați constant cu ciclul de viață al framework-ului și riști să pierzi job-uri la fiecare restart de server.
Controlul prin PM2 și gestiunea resurselor
Într-un proiect de tip microserviciu sau API intern, vreau control total asupra modului în care rulează procesul. Cu un server Node separat și PM2, pot să decid exact câte instanțe rulez în cluster mode pe fiecare nucleu al procesorului. Pot să fac log management ca lumea și să am strategii de restart diferite pentru worker-i față de API-ul principal. La Next.js, ești cam la mâna modului în care framework-ul a decis să abstractizeze request-urile.
Am pățit-o și cu un memory leak subtil la un client. Într-un server Node simplu, l-am găsit relativ repede folosind un heap dump și clinic.js. În Next, cu tot layer-ul de abstractizare, Webpack sub capotă și rutele dinamice, debugging-ul la nivel de memorie a fost un calvar total. Nu știam dacă scurgerea e din codul nostru de business sau din vreo optimizare internă de-a lor.
WebSockets: Durerea de cap evitată
Dacă aplicația ta are nevoie de WebSockets pentru real-time (un chat serios sau un dashboard live), Next.js te obligă la niște workaround-uri urâte. Trebuie să folosești un custom server, ceea ce anulează multe dintre optimizările automate de caching și build ale framework-ului. În schimb, pe un server Node dedicat, ridici un socket.io sau un server de ws în 10 linii de cod și știi sigur că n-o să ai probleme de latență sau conexiuni picate aiurea.
Trade-off-ul pe care trebuie să-l accepți
Nu mă înțelege greșit, nu zic să scrii totul de mână. Dacă ai un CRUD simplu pentru un dashboard de admin, Next.js e imbatabil. Câștigi viteză de dezvoltare imensă. Dar când începi să ai nevoie de procesări care durează peste 5-10 secunde, de WebSockets stabile sau de un consum de memorie predictibil sub sarcină mare, e timpul să separi backend-ul.
Costul? Trebuie să gestionezi două repo-uri (sau un monorepo mai complex), ai două procese de CI/CD și trebuie să te ocupi manual de CORS și autentificare între servicii (JWT-urile ajută aici). Dar stabilitatea pe care o câștigi când UI-ul tău nu mai crapă pentru că cineva a cerut un export CSV masiv merită tot efortul.
Voi cum procedați? Încă încercați să puneți totul în Next sau ați început să mutați 'greul' în servicii dedicate cu Fastify sau NestJS?