
Nest.js Middleware ve Interceptor Kullanımı
Diyarbakır Yazılım
16.08.2026
#Yazılım#Teknoloji#Topluluk
Bir NestJS projesi birkaç controller ve service içerirken request akışını takip etmek kolaydır, fakat uygulama büyüdükçe logging, authentication, validation, response dönüşümü ve hata yönetimi birbirine yaklaşmaya başlar. 10 yıllık backend geliştirme deneyimimde en fazla bakım sorunu çıkaran yapılardan biri, doğru işi yanlış request lifecycle katmanına yerleştirmek oldu. Nest.js Middleware ve Interceptor Kullanımı bu nedenle yalnız iki API'nin nasıl çağrılacağını öğrenmekten ibaret değildir; asıl konu request'in sisteme girişinden response'un kullanıcıya dönmesine kadar sorumlulukları doğru katmanlara dağıtmaktır. Bu rehberde NestJS middleware ve interceptor nasıl kullanılır sorusunu kod seviyesinden production mimarisine kadar ele alırken guard, pipe, filter, RxJS, observability, caching, GraphQL, WebSocket ve microservice senaryolarını da bağlama oturtacağız. Rehber tamamlandığında middleware ile interceptor arasındaki ayrımı yalnız tanım üzerinden değil, gerçek bir production request pipeline tasarlayabilecek kadar net biçimde anlayacaksınız.
NestJS Middleware ve Interceptor Nedir?
Middleware ve interceptor ilk bakışta aynı request üzerinde çalışan iki benzer mekanizma gibi görünür. İkisi de ortak işleri controller kodundan çıkarmaya, tekrarı azaltmaya ve uygulama genelinde belirli davranışları standartlaştırmaya yardımcı olur. Buna rağmen lifecycle içindeki konumları, eriştikleri bilgiler ve response üzerindeki kontrol biçimleri farklıdır. Middleware HTTP request-response döngüsünün erken bölümünde çalışırken interceptor Nest tarafından seçilmiş controller handler'ını çevreleyen bir katman gibi davranır. NestJS middleware ile interceptor arasındaki farklar nelerdir sorusunun en kısa cevabı, middleware'in HTTP akışını ilerletmesi, interceptor'ın ise handler execution ve dönen Observable akışını yönetebilmesidir.
Middleware'in Temel Görevi
Middleware route handler çalışmadan önce request ve response nesnelerine erişen bir fonksiyon veya injectable sınıftır. Request ID üretmek, header normalize etmek, cookie parse etmek veya request context başlatmak gibi route metadata bilgisine ihtiyaç duymayan işler için uygundur. Middleware işlem tamamlandıktan sonra `next()` çağırarak kontrolü bir sonraki aşamaya aktarır. İsterse response'u kendisi tamamlayarak request lifecycle'ı erken de sonlandırabilir. Bu güç nedeniyle middleware içine business rule koymak yerine cross-cutting HTTP sorumlulukları yerleştirmek daha sürdürülebilir bir tasarım oluşturur.
Interceptor'ın Temel Görevi
Interceptor controller veya route handler çalışmadan önce ve çalıştıktan sonra logic ekleyebilen NestJS bileşenidir. `ExecutionContext` sayesinde hangi controller ve handler'ın çalışacağını bilir, bu nedenle middleware'den daha fazla route bağlamına sahiptir. `next.handle()` çağrısının döndürdüğü Observable üzerinde `map`, `tap`, `finalize` veya `catchError` gibi RxJS operatörleri kullanılabilir. Bu yapı response transformation, timing, metrics, serialization ve bazı hata dönüşümleri için güçlü bir alan sağlar. Ayrıca belirli durumlarda `next.handle()` hiç çağrılmadan yeni bir Observable döndürülerek handler execution tamamen atlanabilir.
Middleware ve Interceptor Neden Benzer Görünür?
Her iki yapı da controller içinde tekrar eden ortak görevleri merkezi bir yere taşıdığı için benzer görünür. Logging hem middleware hem interceptor ile yapılabilir ve bu durum yeni başlayanlarda hangi katmanın seçileceği konusunda karışıklık yaratır. Authentication token parse etmek de teknik olarak middleware veya guard tarafında yapılabilir, ancak route metadata kullanılıyorsa guard daha uygun olur. Benzer biçimde response header middleware tarafından değiştirilebilirken handler'ın gerçek dönüş değerini dönüştürmek interceptor'ın doğal görevidir. Katman seçiminde “bu işi yapabilir mi?” yerine “bu iş için gerekli bağlam hangi lifecycle aşamasında mevcut?” sorusunu sormak daha doğru sonuç verir.
Temel Mimari Fark
Temel mimari fark request'in hangi aşamasının kontrol edildiğinde ortaya çıkar. Middleware Nest controller seçimi gerçekleşmeden önce HTTP request akışının parçası olarak çalışır. Interceptor ise Nest execution context oluştuğunda devreye girer ve belirli handler'ı çevreler. Bu nedenle interceptor controller sınıfı, method metadata'sı ve route sonucuyla çalışabilirken middleware bunları doğal olarak bilmez. Mimariyi bu ayrım üzerine kurmak request lifecycle'ın okunabilir ve test edilebilir kalmasını sağlar.
Middleware Request Pipeline'a Girer
Middleware gelen HTTP request'i doğrudan request pipeline içinde karşılar. Express adapter kullanıldığında davranışı Express middleware modeline oldukça yakındır, Fastify adapter kullanıldığında ise underlying platform farkları dikkate alınmalıdır. Request üzerinde correlation ID gibi alanlar hazırlayıp bunları sonraki guard, interceptor, controller ve service katmanlarına taşıyabilir. Middleware'in route sonucunu bilmesine gerek yoktur, çünkü görevi handler execution'ını çevrelemek değildir. Bu nedenle request preprocessing ve erken HTTP normalization işleri middleware için doğal kullanım alanlarıdır.
Interceptor Handler'ı Wrap Eder
Interceptor'ın ayırt edici özelliği handler execution'ını `next.handle()` üzerinden çevrelemesidir. `next.handle()` çağrısından önce çalışan kod request yönündeki davranışı, Observable üzerindeki operator'ler ise handler sonrasındaki davranışı oluşturur. Bu yapı timing, response envelope, cache veya metrics gibi ihtiyaçları aynı noktadan yönetmeyi sağlar. `next.handle()` çağrılmazsa controller method'u hiç çalışmaz ve interceptor kendi response stream'ini döndürebilir. Bu yetenek cache hit gibi handler execution'ın gereksiz olduğu özel senaryolarda bilinçli biçimde kullanılabilir.
NestJS Request Lifecycle Nasıl Çalışır?
NestJS request lifecycle bir request'in sisteme girdiği andan response'un client'a dönmesine kadar çalışan framework bileşenlerinin sırasını açıklar. Genel HTTP akışında middleware önce devreye girer, ardından guards çalışır, interceptor'ların request tarafı açılır, pipes argument processing yapar ve controller handler çağrılır. Handler tamamlandıktan sonra interceptor response yönünde ters sırayla çözülür. Hata oluştuğunda exception filters devreye girerek destekledikleri exception türlerini response contract'a dönüştürebilir. NestJS middleware guard interceptor ve pipe çalışma sırası ve kullanım senaryoları anlaşılmadan büyük bir backend projesinde cross-cutting concern dağılımını doğru yapmak oldukça güçtür.
Incoming Request
Incoming request HTTP adapter tarafından kabul edilen ilk dış girdidir. Bu noktada method, URL, headers, cookies ve body gibi network kaynaklı veriler henüz controller business context'ine ulaşmamıştır. Express veya Fastify adapter request'in temel platform nesnesini Nest katmanına taşır. Request'in güvenilmez kullanıcı girdisi olduğu varsayılmalı ve sonraki katmanlarda gerekli validation ile authorization uygulanmalıdır. Observability açısından request başlangıç zamanı ve correlation ID üretiminin mümkün olduğunca erken yapılması bütün lifecycle'ı takip etmeyi kolaylaştırır.
Middleware
Middleware request pipeline'ın erken aşamasında çalışır ve henüz guard veya interceptor devreye girmemiştir. Birden fazla middleware varsa registration sırasına göre sırayla çalışabilir. NestJS 11'de global module içinde kayıtlı middleware'lerin diğer imported module middleware'lerinden önce çalışması daha belirgin hâle getirilmiştir. Request ID, raw HTTP normalization ve AsyncLocalStorage context başlangıcı bu aşama için iyi örneklerdir. Middleware request'i sonlandırmadığı sürece `next()` çağrısı lifecycle'ın devam etmesi için zorunludur.
Guards
Guards bütün middleware tamamlandıktan sonra ve interceptor veya pipe'lardan önce çalışır. Bir guard `ExecutionContext` üzerinden çalışacak controller ve handler metadata'sını görebilir. Bu özellik authentication sonucu ile route-specific role veya permission bilgisini birleştirmek için önemlidir. Guard `false` döndürür veya exception fırlatırsa handler execution'a geçilmez. Global, controller ve route guard'ları belirli bir sıralamayla çalıştığı için authorization davranışı E2E testlerle doğrulanmalıdır.
Interceptors - Before
Interceptor'ların request yönündeki bölümü guards başarılı olduktan sonra devreye girer. Global interceptor önce, ardından controller ve route interceptor'ları içeri doğru ilerleyen bir wrapper zinciri oluşturabilir. Timing başlangıcı, trace span enrichment veya handler metadata tabanlı ölçümler bu noktada hazırlanabilir. Interceptor henüz controller dönüş değerine sahip değildir, fakat hangi handler'ın çalışacağını bilir. Request yönündeki bu katman response yönünde ters sırada kapanacağı için wrapper mantığı zihinde net tutulmalıdır.
Pipes
Pipes controller argument'ları üzerinde validation ve transformation işlemleri yapar. DTO validation, integer parse veya custom domain input conversion gibi işlemler bu katmana aittir. Pipe başarısız olursa controller method'u çağrılmaz ve hata filter zincirine taşınabilir. Global pipes bütün route argument'larını etkileyebilir, parametre seviyesindeki pipe ise daha dar kapsamlıdır. Input doğrulamasını interceptor veya middleware'e taşımak handler argument lifecycle bilgisini gereksiz yere dağıtır.
Controller
Controller HTTP, GraphQL veya başka transport bağlamından gelen isteği application use-case katmanına yönlendiren giriş noktasıdır. Controller içinde heavy business logic yerine parametreleri almak, service çağırmak ve uygun response contract döndürmek daha sağlıklıdır. Guard ve pipe katmanları tamamlandığı için controller daha güvenilir bir context ile çalışır. Interceptor controller execution'ı çevrelediği için dönüş değeri henüz final network response olmak zorunda değildir. Test edilebilir controller tasarımı underlying Express veya Fastify response nesnesine gereksiz bağımlılığı azaltır.
Service
Service veya use-case katmanı business kurallarının asıl yeridir. Database query, transaction, domain validation ve harici servis orchestration işleri burada veya ilgili application/domain katmanlarında bulunabilir. Middleware veya interceptor içine taşınan business logic çağrı akışını görünmez hâle getirir ve test sınırlarını bozar. Controller service'i çağırdığında request lifecycle framework katmanından business execution'a geçer. Service sonucu tekrar controller üzerinden interceptor response akışına döner.
Interceptors - After
Handler bir değer döndürdüğünde interceptor'ların response tarafı RxJS stream üzerinden devreye girer. Request yönünde en son giren interceptor response yönünde önce çıkar, yani davranış first-in-last-out modeline benzer. `map()` response değerini dönüştürmek, `tap()` side effect üretmek ve `finalize()` success veya error fark etmeksizin cleanup yapmak için kullanılabilir. Serialization, standard response envelope ve latency metric tamamlanması bu aşamada yapılabilir. Global response interceptor tasarlanırken file veya stream gibi özel response türlerinin bozulmaması özellikle kontrol edilmelidir.
Exception Filters
Handler, pipe, guard veya interceptor zincirinde desteklenen bir exception oluştuğunda exception filter devreye girebilir. Filter exception'ı HTTP veya transport-specific response contract'a dönüştürme konusunda framework tarafından sağlanan son kontrol noktalarından biridir. Global error contract oluşturmak için exception filter genellikle interceptor `catchError()` kullanımından daha anlaşılır bir yerdir. Interceptor domain-specific error'ı başka exception'a çevirebilir, filter ise bunun client'a nasıl temsil edileceğini yönetebilir. Hata yönetiminde bu iki görevi ayırmak log ve response davranışını daha öngörülebilir kılar.
Server Response
Lifecycle tamamlandığında Nest üretilen sonucu underlying HTTP adapter üzerinden client'a gönderir. Standard response strategy kullanıldığında framework status, headers ve body üretimini yönetir. Native `@Res()` kullanımı controller'a response üzerinde doğrudan kontrol verir fakat bazı interceptor ve decorator davranışlarını devre dışı bırakabilir. `@Res({ passthrough: true })` belirli header veya cookie işlemleri gerekirken Nest response pipeline'ını korumak için kullanılabilir. Production tasarımında mümkün olduğunca framework response pipeline'ını korumak serialization, testing ve observability açısından avantaj sağlar.
Middleware, Guard, Interceptor, Pipe ve Filter Arasındaki Fark
NestJS'in request lifecycle bileşenleri aynı probleme farklı açılardan çözüm sunmaz, her biri belirli bir sorumluluğa göre tasarlanmıştır. Middleware request'i erken hazırlar, guard erişime karar verir, interceptor handler'ı çevreler, pipe input'u doğrular veya dönüştürür ve filter exception'ı response'a map eder. Aynı işi birden fazla katmanda teknik olarak gerçekleştirebilmek doğru mimari olduğu anlamına gelmez. Katman seçimini handler metadata ihtiyacı, response değerine erişim, input argument bilgisi ve error ownership belirlemelidir. Bu ayrım ekip içinde kod review standardına dönüştüğünde yeni feature'ların request pipeline'a rastgele dağılması büyük ölçüde önlenir.
Middleware
Middleware HTTP request ve response nesnelerine en erken erişim sağlayan NestJS yapı taşlarından biridir. Correlation ID, locale veya raw request preprocessing gibi controller bilgisine ihtiyaç duymayan işler burada uygulanabilir. Middleware route handler metadata'sını bilmediği için roles veya permission kararını burada vermek uygun değildir. `next()` çağrısı request'i devam ettirirken response'u doğrudan bitirmek lifecycle'ı erken sonlandırabilir. Middleware mümkün olduğunca hafif ve cross-cutting HTTP concern odaklı tutulmalıdır.
Guard
Guard bir request'in belirli handler'a girmesine izin verilip verilmeyeceğini belirler. `ExecutionContext` üzerinden route ve controller metadata'sını okuyabildiği için role, permission ve policy tabanlı authorization için doğru katmandır. Authentication token parse işleminin bir bölümü middleware veya passport strategy içinde yapılabilir, fakat route admission kararı guard tarafından yönetilebilir. Guard başarısız olduğunda controller ve pipe execution gerçekleşmez. Bu net görev guard'ı security pipeline'ın karar noktası hâline getirir.
Interceptor
Interceptor handler öncesi ve sonrası logic için wrapper görevi görür. ExecutionContext ile controller ve method bilgisine, CallHandler ile de response stream'e erişir. Timing, tracing, caching, serialization ve response transformation bu nedenle interceptor kullanımına uygundur. Authentication veya DTO validation gibi lifecycle'ın başka bileşenlerine ait işleri interceptor'a taşımak sorumluluk sınırlarını bozar. Interceptor cross-cutting handler davranışını merkezi ve tekrar kullanılabilir biçimde yönetmelidir.
Pipe
Pipe controller method argument'ları üzerinde çalışır. Gelen string parametresini number'a çevirmek veya DTO'yu class-validator tabanlı doğrulamak tipik örneklerdir. Pipe route handler'a gönderilecek değerin geçerli ve beklenen biçimde olmasını sağlar. Validation başarısız olduğunda handler çağrılmadan exception üretilebilir. Input contract ile ilgili bir sorun middleware veya interceptor yerine pipe katmanında tutulduğunda kodun amacı daha açık hâle gelir.
Exception Filter
Exception filter framework veya application tarafından fırlatılan exception'ları yakalayıp transport response'una dönüştürür. HTTP status, error code, message ve correlation ID gibi ortak error contract alanları burada standardize edilebilir. Filter exception type'a göre özel davranabilir. Business rule oluşturmak veya başarılı response dönüştürmek filter'ın işi değildir. Global error contract tek bir filter ailesi altında tutulduğunda client uygulamalarının hata işleme davranışı daha kararlı olur.
Hangi İş Hangi Katmana Konmalıdır?
Request ID ve header normalization middleware'e, authorization guard'a, DTO validation pipe'a ve response transformation interceptor'a konmalıdır. Global error contract exception filter tarafından yönetilmelidir. Business logic ise service veya use-case katmanında kalmalıdır. Eğer bir concern controller metadata'sına ihtiyaç duyuyor fakat handler dönüş değerini kullanmıyorsa guard veya interceptor arasındaki amaç farkı tekrar değerlendirilmelidir. Mimari kararların kısa bir request pipeline dokümanında kayıtlı olması ekipte tutarlı implementation sağlar.
NestJS Middleware Nasıl Çalışır?
NestJS middleware bir function veya `NestMiddleware` interface'ini uygulayan injectable class olarak oluşturulabilir. Request, response ve `next` parametreleri underlying HTTP adapter'ın middleware modeline göre kullanılır. Middleware bir module içinde `MiddlewareConsumer` ile belirli route'lara bağlanabilir veya `app.use()` ile global olarak eklenebilir. Dependency injection gereken kullanımda class-based module middleware daha uygun olur. Middleware'in ne zaman request'i bitirdiği ve ne zaman `next()` çağırdığı açık değilse production'da askıda kalan istekler oluşabilir.
NestMiddleware Interface
`NestMiddleware` class-based middleware için standart sözleşmeyi tanımlar. Class genellikle `@Injectable()` ile işaretlenir ve `use()` method'u request lifecycle'a giriş noktası olur. Interface TypeScript geliştirme sırasında doğru middleware signature konusunda yönlendirme sağlar. Express request type'larını doğrudan kullanmak adapter bağımlılığı oluşturabileceği için reusable paketlerde dikkatli olunmalıdır. Basit bir application-specific Express projesinde ise explicit Request ve Response type'ları geliştirici deneyimini iyileştirebilir.
use() Metodu
`use()` method'u middleware çalıştığında framework tarafından çağrılır. Method içinde request header okunabilir, response header eklenebilir veya request nesnesine context alanı yerleştirilebilir. Asenkron bir işlem yapılacaksa gecikmenin bütün request latency'ye ekleneceği unutulmamalıdır. Middleware business data almak için her request'te database query çalıştıran bir katmana dönüştürülmemelidir. İş tamamlandığında request'i devam ettirmek için `next()` kontrollü biçimde çağrılır.
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
@Injectable()
export class RequestIdMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const requestId =
req.header('x-request-id') ?? crypto.randomUUID();
req.headers['x-request-id'] = requestId;
res.setHeader('x-request-id', requestId);
next();
}
}
Request
Request nesnesi method, path, headers, cookies, body ve connection bilgilerini taşır. Middleware gelen request'i normalize edebilir fakat doğrulanmamış user input'un güvenilir olduğu varsayılmamalıdır. Request üzerinde custom property eklemek gerekiyorsa TypeScript type augmentation veya ayrı request context çözümü kullanılabilir. Büyük uygulamalarda her concern'in request nesnesine yeni property yazması bakım maliyetini artırabilir. Correlation veya tenant context için AsyncLocalStorage bu nedenle bazı projelerde daha temiz bir alternatif sunar.
Response
Response nesnesi middleware'in header eklemesine veya request-response cycle'ı erken tamamlamasına izin verir. Security headers, request ID echo veya bazı low-level HTTP işlemleri bu noktada yapılabilir. Middleware response body'yi doğrudan gönderirse sonraki Nest handler'ları çalışmaz. Framework serialization ve interceptor akışı isteniyorsa middleware response'u tamamlamamalıdır. Native response kullanımının adapter bağımlılığı getirdiği de reusable kod tasarımında hesaba katılmalıdır.
next()
`next()` middleware zincirindeki sonraki aşamaya kontrolü aktarır. Middleware request'i kendisi sonlandırmıyorsa bu çağrı yapılmalıdır. Bir middleware asenkron iş bitmeden `next()` çağırırsa sonraki lifecycle aşamaları beklenenden erken çalışabilir. Buna karşılık gereksiz uzun asenkron işlemden sonra çağrılması bütün endpoint latency'sini artırır. `next()` kullanımını net bir success path üzerinde tutmak hata ayıklamayı kolaylaştırır.
next() Çağrılmazsa Ne Olur?
Middleware response'u kendisi tamamlamadığı hâlde `next()` çağırmazsa request pipeline ilerlemez. Client genellikle timeout'a kadar açık kalan bir bağlantı görür. Controller, guard ve interceptor hiç çalışmadığı için sorun application loglarında beklenmedik derecede sessiz olabilir. E2E testte middleware'den geçen route'un beklenen response'u verdiğini doğrulamak bu hatayı kolayca yakalar. Early return kullanılan middleware'lerde her branch'in response sonlandırdığı veya `next()` çağırdığı code review sırasında kontrol edilmelidir.
Class-Based Middleware Nasıl Yazılır?
Class-based middleware dependency injection veya daha yapılandırılmış state yönetimi gerektiğinde tercih edilir. Class `@Injectable()` ile tanımlanır ve `NestMiddleware` sözleşmesini uygulayabilir. Logger, configuration service veya request-context service constructor üzerinden inject edilebilir. Middleware class'ı module scope içinde kullanılabilir provider'lara erişebilir. Basit tek satırlık logic için class oluşturmak gereksiz olabilir, fakat production observability gibi bağımlılık gerektiren concern'lerde class yapısı daha yönetilebilir olur.
@Injectable()
`@Injectable()` class'ın Nest dependency injection container tarafından yönetilebilmesini sağlar. Constructor dependency'leri framework tarafından resolve edilir. Request logger veya config provider gibi bileşenler middleware'e bu yolla aktarılabilir. Class instance lifecycle normal provider davranışıyla uyumlu tutulmalıdır. Global `app.use()` ile doğrudan verilen functional middleware'in aynı module DI bağlamına sahip olmadığı unutulmamalıdır.
NestMiddleware Implementasyonu
`NestMiddleware` interface'i class'ın `use()` method'u sağlamasını bekler. Interface runtime davranışı değiştirmez, fakat TypeScript compile aşamasında sözleşmeyi açıklar. Middleware'in request ve response type'ları kullanılan adapter'a göre değişebilir. Adapter-agnostic package geliştiriliyorsa platform-specific alanlardan kaçınmak önemlidir. Application yalnız Express kullanıyorsa doğrudan Express type'ları daha pratik olabilir.
Dependency Injection
Class middleware aynı module bağlamında kullanılabilir dependency'leri inject edebilir. Structured logger, feature configuration veya context factory buna örnektir. Request başına database lookup yapan dependency'yi middleware'e eklemek latency ve hidden coupling üretebilir. Dependency graph basit tutulduğunda middleware startup ve testing davranışı da daha anlaşılır olur. DI gereksinimi varsa `app.use()` yerine module middleware registration tercih etmek genellikle daha doğrudur.
Class Middleware Ne Zaman Kullanılmalı?
Middleware logger veya configuration service kullanıyorsa class-based yaklaşım iyi seçimdir. Birden fazla private helper method veya test edilmesi gereken ayrı davranış bulunduğunda class yapısı okunabilirliği artırır. Tek satırlık header ekleme fonksiyonu için class zorunlu değildir. Reusable middleware package geliştiriliyorsa constructor injection capability önemli avantaj sağlayabilir. Karar verirken syntax tercihi yerine gerçek dependency ve test ihtiyacı esas alınmalıdır.
Functional Middleware Nasıl Yazılır?
Functional middleware plain function olarak tanımlanır ve özel bir Nest interface veya decorator gerektirmez. Request, response ve next parametrelerini alır, gerekli işi yapar ve zinciri ilerletir. Dependency gerektirmeyen basit request normalization için düşük kod yükü sunar. Aynı function `MiddlewareConsumer.apply()` ile route'lara bağlanabilir. Basit concern'lerde functional yaklaşım gereksiz class ve provider tanımlarını azaltır.
Function-Based Middleware
Function-based middleware en küçük middleware formudur. Örneğin basit bir header normalization veya development logger birkaç satırlık fonksiyonla uygulanabilir. Function stateless tutulduğunda unit test etmek de kolaydır. DI container'dan provider alması gerekiyorsa class middleware daha uygun hâle gelir. Global `app.use()` kullanımında function doğrudan application instance'a verilebilir.
Class Middleware ile Farkı
Temel davranış açısından her ikisi de request pipeline içinde aynı amaca hizmet edebilir. Class middleware Nest DI özelliklerinden doğal biçimde faydalanırken functional middleware daha hafif yapı sunar. Class method'ları ve encapsulated state karmaşık davranışı düzenlemeye yardımcı olabilir. Function küçük ve dependency-free concern için daha açık okunabilir. Ekip standardı iki yaklaşımı kullanım amacına göre tanımlarsa tutarsız middleware yapıları azalır.
Dependency Gerekmeyen Middleware
Request header değerini lowercase etmek veya basit bir response header eklemek için dependency gerekmeyebilir. Böyle durumlarda functional middleware doğrudan problemi ifade eder. Config veya logger enjekte etmek için global variable kullanmaya başlamak ise class middleware'e geçiş sinyalidir. Pure function yapısı test input-output davranışını net tutar. Security-sensitive logic basit görünse bile merkezi config veya audit ihtiyacı varsa DI değerlendirilmelidir.
Hangi Yaklaşım Daha Basittir?
Dependency yoksa functional middleware genellikle daha basittir. Dependency, lifecycle veya helper method gereksinimi varsa class middleware kısa sürede daha okunabilir hâle gelir. “Her middleware class olmalı” veya “function her zaman daha iyi” gibi tek kural yoktur. Kodun amacı ilk bakışta anlaşılmalı ve test setup gereksiz büyümemelidir. Basitlik yalnız satır sayısıyla değil future maintenance maliyetiyle değerlendirilmelidir.
MiddlewareConsumer Nasıl Kullanılır?
`MiddlewareConsumer` module seviyesinde middleware'in hangi route'lara uygulanacağını belirleyen Nest API'sidir. `configure()` method'u içinde `apply()`, `exclude()` ve `forRoutes()` zinciriyle middleware scope açık biçimde tanımlanabilir. Controller class, path veya HTTP method bazlı route selection yapılabilir. Bu yöntem DI kullanan middleware'leri module context içinde kaydetmek için de uygundur. Büyük projelerde middleware registration'ı ilgili module yakınında tutmak hangi request'lerin etkilendiğini anlamayı kolaylaştırır.
configure()
Middleware kullanan module class genellikle `NestModule` implement eder ve `configure()` method'u tanımlar. Framework application bootstrap sırasında bu method üzerinden middleware registration bilgisini toplar. Method async olarak da tanımlanabilir, fakat registration aşamasında gereksiz network call yapmak tavsiye edilmez. Configuration declarative ve hızlı tutulmalıdır. Middleware sırası önemliyse registration order E2E lifecycle testinde doğrulanmalıdır.
consumer.apply()
`consumer.apply()` bir veya daha fazla middleware'i sırayla registration zincirine ekler. Aynı route için birden fazla middleware verildiğinde sequence önem kazanır. Correlation ID middleware logger middleware'den önce çalışacak şekilde düzenlenebilir. Farklı concern'leri tek dev middleware'e birleştirmek yerine küçük composable katmanlar daha kolay test edilir. Apply zinciri sonrasında `forRoutes()` ile scope belirtilmeden registration tamamlanmaz.
forRoutes()
`forRoutes()` middleware'in hangi route veya controller'larda çalışacağını tanımlar. String path, RouteInfo veya controller class kullanılabilir. NestJS 11 wildcard route matching davranışı nedeniyle named wildcard syntax güncel projelerde tercih edilmelidir. Root dahil wildcard için `{*splat}` benzeri yapı kullanılabilir. Route scope açık yazıldığında middleware'in beklenmeyen endpoint'leri etkileme riski azalır.
Controller Bazlı Middleware
Controller class `forRoutes()` içine verildiğinde controller altındaki route'lar middleware kapsamına alınır. Bu yaklaşım feature module bazlı concern'lerde okunabilir bir registration sağlar. Belirli method'ları hariç tutmak için `exclude()` eklenebilir. Controller path değiştiğinde class reference route string'e göre daha dayanıklı olabilir. Middleware business feature'a özgü değilse global veya daha üst module scope daha uygun olabilir.
Route Bazlı Middleware
Belirli path yalnız bir middleware concern gerektiriyorsa route bazlı registration yapılabilir. Health endpoint'i veya webhook endpoint'i buna örnek olabilir. Route string'in Express 5 wildcard semantics ile uyumu NestJS 11 migration sırasında kontrol edilmelidir. Çok fazla tekil path registration maintenance maliyetini artırabilir. Benzer route gruplarında controller veya named wildcard daha temiz olabilir.
HTTP Method Bazlı Middleware
`RouteInfo` kullanılarak path ile birlikte `RequestMethod.GET`, `POST` veya başka method seçilebilir. Aynı path'in yalnız write operasyonlarında özel middleware çalıştırması gerektiğinde faydalıdır. Method filtering authorization yerine low-level request concern için kullanılmalıdır. Aynı path ve method başka module tarafından tanımlanıyorsa scope dikkatle test edilmelidir. Registration davranışı integration testte gerçek HTTP request ile doğrulanabilir.
Middleware Route'ları Nasıl Hariç Tutulur?
Bir middleware geniş route grubuna uygulanırken belirli endpoint'lerin etkilenmemesi gerekebilir. `exclude()` bu ihtiyacı registration zinciri içinde karşılar. Public endpoint, health check veya webhook gibi route'lar farklı request processing gerektirebilir. Exclusion listesi büyüyüp sürekli değişiyorsa middleware scope tasarımının fazla geniş olup olmadığı tekrar değerlendirilmelidir. Named wildcard syntax NestJS 11 ve Express 5 migration sırasında exclusion path'lerinde de kontrol edilmelidir.
exclude()
`exclude()` string path veya RouteInfo nesneleri kabul edebilir. Birden fazla exclusion aynı registration zincirinde tanımlanabilir. Wildcard pattern'ler path-to-regexp davranışına göre eşleşir. Exclusion route'un gerçekten bypass edildiği E2E testle doğrulanmalıdır. Security concern'i exclude ederken yanlış path pattern ciddi erişim riski yaratabileceği için daha dikkatli review gerekir.
Public Endpoint'ler
Public endpoint'ler bazı authentication parsing middleware'lerinden muaf tutulabilir, fakat çoğu zaman token parse işleminin zarar vermeden çalışması daha basit olabilir. Authorization kararı guard metadata'sıyla yönetildiğinde public route istisnaları daha okunabilir olur. Middleware exclusion yalnız gerçekten teknik olarak gerekli olduğunda kullanılmalıdır. Public route logging ve correlation ID gibi observability concern'lerinden çıkarılmamalıdır. Böylece anonymous traffic de production trace'lerinde izlenebilir.
Health Check Endpoint'leri
Health endpoint yüksek frekansta orchestrator tarafından çağrılabilir. Ağır logging, tenant resolution veya database lookup yapan middleware burada gereksiz maliyet yaratabilir. Buna rağmen request ID veya basit access logging işletim ihtiyaçlarına göre korunabilir. Liveness endpoint business dependency'lere bağlanmamalıdır. Exclusion seçimi health probe'un amacı ve request hacmine göre yapılmalıdır.
Wildcard Route'lar
Wildcard exclusion bir path ağacını tek kural ile kapsam dışında bırakabilir. NestJS 11 ve Express 5 route syntax değişiklikleri nedeniyle eski `*` pattern'ler migration sırasında gözden geçirilmelidir. Named wildcard hangi parçanın eşleştiğini daha açık ifade eder. Root path'in dahil edilip edilmediği braces kullanımına göre değişebilir. E2E test root ve nested path örneklerini ayrı ayrı doğrulamalıdır.
NestJS 11'de Middleware Wildcard Syntax
NestJS 11'e geçişte middleware route matching en dikkat edilmesi gereken uyumluluk alanlarından biridir. Express 5'in yeni path-to-regexp davranışı adlandırılmamış wildcard kullanımını daha sıkı hâle getirmiştir. Nest belirli eski pattern'ler için compatibility dönüşümleri sağlayabilse de yeni kodda named wildcard syntax kullanmak daha güvenlidir. Fastify 5 desteği de NestJS 11 ile gelmiş, middleware matching tarafında yeni path-to-regexp davranışı kullanılmaya başlanmıştır. Migration sırasında route controller pattern'leri kadar `MiddlewareConsumer`, exclusion ve global-like middleware registration pattern'leri de test edilmelidir.
Express 5 Değişiklikleri
NestJS 11 varsayılan Express adapter tarafında Express 5 kullanır. Express 5 route matching, eski Express 4 pattern'lerinden bazılarını artık aynı biçimde kabul etmez. Wildcard karakterinin bir isim taşıması beklenir. Optional segment syntax ve bazı regexp karakter davranışları da değişmiştir. Nest migration sırasında route ve middleware testleri bu nedenle yalnız compile başarısına bırakılmamalıdır.
* Kullanımındaki Değişiklik
Tek başına veya isim verilmeden kullanılan wildcard Express 5'in önerilen syntax'ı değildir. Nest bazı basit eski kullanımları compatibility için dönüştürebilir, ancak bunu yeni kod standardı olarak görmek doğru değildir. Named wildcard pattern migration sonrasında davranışı daha açık hâle getirir. Middleware `forRoutes('*')` gibi eski örnekler özellikle gözden geçirilmelidir. Root path ve nested path eşleşmesi ayrı test edilmelidir.
*splat
`*splat` adlandırılmış wildcard örneğidir. `splat` kelimesinin özel framework anlamı yoktur ve farklı geçerli bir isim kullanılabilir. Bu biçim wildcard segmentinin bulunduğu alt path'leri eşleştirmek için kullanılabilir. Root path tek başına eşleşmeyebilir. Bu nedenle gerçek requirement root dahil bütün route'lar ise optional wildcard biçimine geçmek gerekir.
{*splat}
`{*splat}` wildcard bölümünü optional grup içine alır. Bu sayede wildcard path bulunmasa bile root path eşleşebilir. Global-like module middleware registration için bu davranış sık gerekir. Eski `forRoutes('*')` migration'ında doğrudan aynı davranış bekleniyorsa braces farkı önemlidir. Root ve nested URL örnekleri integration test ile birlikte kontrol edilmelidir.
Root Path Dahil Wildcard
Root path dahil wildcard kullanımında optional group gereklidir. Örneğin yalnız `*splat` nested path'i eşleştirirken `{*splat}` root'u da kapsayabilir. Prefix bulunan route'larda `users/{*splat}` benzeri pattern düşünülür. Pattern'in Express ve Fastify adapter'da beklenen biçimde çalıştığı doğrulanmalıdır. Framework upgrade öncesinde route coverage testleri bu tür farklılıkları erken yakalar.
Eski NestJS Projelerini Güncelleme
NestJS 10 veya daha eski projelerde route wildcard kullanımını repository genelinde aramak iyi başlangıçtır. Controller route, middleware `forRoutes`, `exclude` ve global prefix exclusion pattern'leri birlikte kontrol edilmelidir. Node.js runtime NestJS 11 için 20 veya daha güncel desteklenen sürüme çıkarılmalıdır. Express-specific query parser davranışı kullanan projeler nested query parsing değişikliğini de test etmelidir. Migration gerçek HTTP E2E testleriyle tamamlandığında yalnız type-level uyumluluğa güvenilmemiş olur.
Global Middleware Nasıl Tanımlanır?
Bütün request'lerde çalışması gereken basit middleware `app.use()` ile application bootstrap sırasında global olarak eklenebilir. Bu yöntem underlying HTTP middleware'i doğrudan application instance'a bağlar. Request ID veya çok basit normalization gibi dependency-free concern'lerde kullanışlıdır. Ancak module dependency injection container'a erişim doğrudan `app.use()` registration içinde sınırlıdır. DI gereken global davranış için class middleware'i module üzerinden bütün route'lara bağlamak daha uygun bir tasarımdır.
app.use()
`app.use()` Nest application instance'ın underlying platform middleware mekanizmasına erişim sağlar. Functional middleware doğrudan burada kaydedilebilir. Registration bootstrap aşamasında yapıldığı için module-level DI bağlamında değildir. Platform-specific middleware package kullanılıyorsa Express ve Fastify compatibility ayrıca incelenmelidir. Global davranış küçük ve düşük maliyetli tutulmalıdır çünkü her request bundan etkilenir.
Tüm Route'lara Uygulama
Global middleware bütün route'lara ve çoğu zaman health veya internal endpoint'lere de uygulanır. Bu nedenle expensive logging veya body processing global katmanda dikkatle kullanılmalıdır. Route istisnası gerekiyorsa module middleware ve `exclude()` daha fazla kontrol sunabilir. Correlation ID gibi her request için değerli concern'ler global kullanım için uygundur. E2E test farklı module route'larında middleware'in gerçekten çalıştığını doğrulamalıdır.
Global Middleware'de DI Sınırları
`app.use()` ile application dışından oluşturulan middleware Nest module DI container'ına normal provider gibi bağlanmaz. Logger veya config service'i manuel resolve etmek code smell oluşturabilir. Bu ihtiyaç ortaya çıktığında registration stratejisi yeniden düşünülmelidir. DI gerektirmeyen pure middleware global application seviyesinde rahat kullanılabilir. DI ağırlıklı concern'ler module-scoped class middleware'e taşınmalıdır.
DI Gerekiyorsa Module Middleware Kullanımı
Class middleware ilgili module içinde provider dependency'leri inject edebilir. `MiddlewareConsumer` ile named wildcard üzerinden bütün route'lara uygulanabilir. Bu yaklaşım structured logger veya AsyncLocalStorage service gibi provider'ların kullanımını kolaylaştırır. Registration order NestJS 11 global module davranışıyla birlikte test edilmelidir. Global ihtiyaç olması middleware'in mutlaka `app.use()` ile tanımlanması gerektiği anlamına gelmez.
Middleware İçin En Yaygın Kullanım Alanları
Middleware için en uygun görevler request lifecycle'ın erken aşamasında yapılması gereken ve route handler metadata'sına bağımlı olmayan işlerdir. Request logging, request ID, cookie parse, locale detection ve tenant context başlangıcı yaygın örneklerdir. Webhook raw body veya security header ihtiyacı da HTTP seviyesinde çözülebilir. Bu concern'ler business service katmanına dağıtıldığında aynı logic onlarca controller veya service içinde tekrar eder. Middleware'in gücü ortak request preprocessing'i merkezi hâle getirirken business kararlarını kendisinden uzak tutmasındadır.
Request Logging
Middleware request geldiği anda method, path ve correlation ID gibi temel bilgileri kaydedebilir. Guard request'i daha sonra reddetse bile başlangıç logu bulunur. Buna karşılık controller ve handler metadata'sı middleware seviyesinde doğal olarak mevcut değildir. Detaylı handler duration için interceptor daha anlamlı olabilir. Production'da iki katman ortak structured logger üzerinden birbiriyle bağlantılı kayıtlar üretmelidir.
Request ID
Request ID her incoming request için benzersiz bir kimlik sağlar. Client güvenilir bir request ID göndermiyorsa middleware yeni UUID üretebilir. Aynı ID response header'a ve log context'e yazılabilir. Downstream HTTP veya message request'lerinde propagate edilmesi distributed troubleshooting'i kolaylaştırır. Kullanıcı tarafından gelen ID formatı ve uzunluğu abuse riskine karşı validate edilmelidir.
Correlation ID
Correlation ID bir business request'in birden fazla service arasındaki izini takip etmek için kullanılır. Tek bir incoming request downstream beş service çağrısı oluşturduğunda aynı correlation bilgisi logları birleştirebilir. Trace ID ile correlation ID aynı kavram olmak zorunda değildir. Middleware erken aşamada değeri oluşturup AsyncLocalStorage içine koyabilir. Structured logger her mesajda bu değeri otomatik eklediğinde developer'ın manuel parametre geçmesine gerek kalmaz.
Cookie Parsing
Cookie parsing HTTP header bilgisini kolay kullanılabilir request alanlarına dönüştürür. Underlying adapter veya ilgili plugin davranışı platforma göre farklı olabilir. Authentication session cookie kullanılıyorsa parsing authentication decision'dan önce tamamlanmalıdır. Cookie değerleri loglanmamalı ve sensitive credential kabul edilmelidir. Signed veya encrypted cookie doğrulaması security standardına göre ayrı bileşenle yapılmalıdır.
Header Normalization
Client veya proxy kaynaklı header değerleri application genelinde ortak forma dönüştürülebilir. Örneğin locale veya custom tenant header boşluk ve case davranışı normalize edilebilir. Security-sensitive header'ın client tarafından overwrite edilmesine izin verilmemelidir. Reverse proxy tarafından üretilen trusted header'lar için proxy trust configuration ayrıca önemlidir. Normalization business validation yerine yalnız HTTP format standardizasyonu yapmalıdır.
Locale Detection
`Accept-Language` veya custom locale header middleware tarafından okunabilir. Seçilen locale request context içine yazılarak service veya presentation katmanında kullanılabilir. Desteklenmeyen locale default değere normalize edilebilir. Locale business data lookup gerektiriyorsa bu logic ayrı service katmanında tutulmalıdır. Aynı locale bilgisini her controller'da tekrar parse etmek yerine request başlangıcında merkezi seçim daha tutarlıdır.
Tenant Context
Multi-tenant uygulamalarda tenant ID host, subdomain, header veya token claim üzerinden bulunabilir. Middleware yalnız ham tenant identifier çıkarımı yapabilir, fakat tenant'ın belirli route'a erişim hakkı authorization guard tarafından doğrulanmalıdır. Tenant context AsyncLocalStorage içine yerleştirilebilir. Client-controlled header doğrudan güvenilir tenant identity olarak kabul edilmemelidir. Database query'lerde tenant filter'ın repository veya data access standardı üzerinden zorunlu uygulanması daha güvenlidir.
Raw Body Capture
Webhook signature doğrulaması serialized JSON değil request'in orijinal byte dizisini gerektirebilir. Nest uygulaması `rawBody: true` seçeneğiyle Express veya Fastify üzerinde raw body erişimini destekler. Body parser sırası yanlış tasarlanırsa signature verification için gereken içerik değişebilir. Raw body gereksiz bütün endpoint'lerde memory'de tutulmamalıdır. Webhook endpoint scope ve payload size limitleri güvenlik açısından ayrıca belirlenmelidir.
Security Headers
Security header'lar response'un browser davranışını sınırlamaya yardımcı olabilir. Helmet benzeri middleware veya adapter-specific çözüm global uygulanabilir. Content Security Policy gibi header'lar application'ın gerçek asset ve API kullanımına göre yapılandırılmalıdır. Varsayılan set'i anlamadan production'a kopyalamak login veya frontend kaynak yüklemelerini bozabilir. Header policy security test ve browser integration testleriyle doğrulanmalıdır.
Request ID Middleware Nasıl Tasarlanır?
İyi bir Request ID middleware yalnız rastgele UUID üretmekle kalmaz, client'tan gelen değerin güvenilirlik sınırını da tanımlar. Kabul edilen ID formatı sınırlandırılabilir ve boş veya aşırı uzun değerlerde yeni UUID oluşturulabilir. Oluşturulan ID response header, AsyncLocalStorage ve structured logger'a aynı anda aktarılır. Downstream service çağrılarında aynı identifier propagate edilirse distributed incident analizi kolaylaşır. Request ID uygulamanın security token'ı değildir ve authorization kararında kullanılmamalıdır.
Client'tan Gelen Request ID
API gateway veya trusted upstream mevcut request ID gönderebilir. Middleware bu değeri format ve uzunluk açısından kontrol edebilir. Public client'ın sınırsız string göndermesine izin vermek log injection veya yüksek cardinality sorunu oluşturabilir. Trusted proxy ile public client header'ı birbirinden ayırt edilmelidir. Uygun olmayan değer yeni server-generated ID ile değiştirilmelidir.
Yeni UUID Oluşturma
Geçerli request ID bulunmuyorsa `crypto.randomUUID()` gibi güvenilir yöntemle yeni identifier üretilebilir. Random değer yeterince benzersiz olmalıdır. Request ID sıralı database primary key olarak kullanılmamalıdır. ID production loglarında kişisel veri taşımamalıdır. Aynı request boyunca bir kez oluşturulup bütün katmanlarda tekrar kullanılmalıdır.
Response Header'a Yazma
Response header'a request ID eklemek client support süreçlerini kolaylaştırır. Kullanıcı hata raporunda bu ID'yi ilettiğinde loglar hızlı bulunabilir. Header adı ekip standardında sabit tutulmalıdır. Reverse proxy aynı header'ı overwrite ediyorsa ownership açıkça belirlenmelidir. Response ID ile internal trace ID farklıysa ikisini karıştırmamak gerekir.
Log'lara Aktarma
Structured logger request ID'yi her log event'ine otomatik eklemelidir. Developer'ın her `logger.info()` çağrısında ID parametresi geçmesi güvenilir değildir. AsyncLocalStorage veya request-scoped logger context bu otomasyonu sağlayabilir. High-cardinality identifier metric label olarak kullanılmamalıdır. Log araması için değerli olan request ID, Prometheus label için pahalı olabilir.
Microservice'lere Propagate Etme
HTTP downstream request'te correlation header, Kafka veya RabbitMQ message'ında metadata field kullanılabilir. Her transport için propagation standardı belirlenmelidir. Incoming external ID güvenilir değilse internal correlation ID ayrı üretilmesi tercih edilebilir. Service yeni child trace oluştururken correlation relationship korunur. Cross-service E2E test header veya metadata'nın gerçekten taşındığını doğrulamalıdır.
AsyncLocalStorage ile Request Context
Node.js `AsyncLocalStorage` aynı asynchronous execution zinciri içinde request'e özel state taşımayı mümkün kılar. NestJS'te middleware request lifecycle'ın erken aşamasında context'i başlatmak için uygun bir noktadır. Correlation ID, tenant ID veya trace bilgisi service method'larına ayrı parametre olarak gönderilmeden erişilebilir. Bu rahatlık implicit dependency oluşturabileceği için store sınırsız bir global context nesnesine dönüştürülmemelidir. Request context yalnız cross-cutting ve gerçekten request-bound bilgileri tutacak kadar küçük kalmalıdır.
AsyncLocalStorage Nedir?
`AsyncLocalStorage` Node.js `async_hooks` altyapısı üzerinde request veya async call chain'e özel store sağlayan API'dir. Başka dillerdeki thread-local storage kavramına benzer bir kullanım sunar, ancak Node asynchronous execution modeline göre çalışır. `run()` ile oluşturulan store o call chain içindeki sonraki asynchronous işlemler tarafından okunabilir. Nest doğrudan özel bir AsyncLocalStorage abstraction sunmaz, fakat provider ve middleware ile kolayca entegre edilebilir. Third-party CLS paketleri ek convenience sağlayabilir, ancak core Node API davranışını anlamak debugging için değerlidir.
Request Context'i Middleware'de Başlatmak
Middleware request pipeline'ın erken aşamasında olduğu için `als.run(store, () => next())` modeli bütün sonraki Nest enhancer ve service'leri aynı context içine alabilir. Store içine request ID gibi başlangıç alanları yazılır. `next()` call ALS callback içinde yapılmalıdır. Context başlatma middleware'i başka logger middleware'lerinden önce çalıştırılırsa loglar ilk andan itibaren correlation bilgisine sahip olur. Lifecycle order değişiklikleri integration test ile doğrulanmalıdır.
Correlation ID
Correlation ID AsyncLocalStorage için en yaygın kullanımlardan biridir. Logger her log yazımında store'dan ID okuyabilir. Service method'ları request nesnesi hakkında bilgi sahibi olmak zorunda kalmaz. Background job request lifecycle dışında çalışıyorsa ayrı context başlatmalıdır. Store bulunmayan durumlar logger tarafından güvenli fallback ile yönetilmelidir.
User/Tenant Context
Authenticated user veya tenant bilgisi guard sonrasında belli olabilir. Middleware başlattığı ALS store'u daha sonra guard veya interceptor tarafından güncellenebilir. Service bu bilgiyi audit log için okuyabilir, fakat authorization kararını gizli implicit context'e tamamen bırakmak risklidir. Repository tenant filtering explicit architecture standardıyla korunmalıdır. Context business method signature'larını gereksiz sadeleştirirken davranışı görünmez hâle getirmemelidir.
Structured Logging ile Kullanım
Logger her log event'inde AsyncLocalStorage store'daki request ID, tenant ve trace ID alanlarını otomatik ekleyebilir. JSON log formatı merkezi log platformunda filtreleme ve correlation sağlar. Password, token veya sensitive payload context içine yazılmamalıdır. Store alanlarının isimleri service'ler arasında standardize edilmelidir. Logging benchmark yapılarak her event'te ağır serialization maliyetinden kaçınılmalıdır.
Webhook'larda Middleware Kullanımı
Webhook endpoint'leri normal JSON API request'lerinden farklı olarak çoğu zaman request'in raw body değerine ihtiyaç duyar. Provider signature değeri orijinal byte dizisi üzerinden hesaplandığı için parse edilmiş object aynı sonucu vermeyebilir. Nest `rawBody: true` application seçeneğiyle Express ve Fastify adapter üzerinde raw body erişimi sağlayabilir. Signature verification controller veya dedicated guard/service içinde yapılabilir, ancak raw payload'ın korunması body parser aşamasında çözülmelidir. Webhook güvenliği timestamp, replay protection ve secret rotation gibi ek kontrollerle birlikte ele alınmalıdır.
Raw Body Neden Gereklidir?
HMAC tabanlı webhook signature genellikle gönderilen HTTP body'nin tam byte dizisi üzerinden üretilir. JSON parse ve tekrar stringify işlemi property order veya whitespace değiştirerek signature sonucunu bozabilir. Bu nedenle `req.rawBody` benzeri orijinal buffer gereklidir. Payload size limit uygulanmazsa saldırgan büyük body ile memory tüketebilir. Raw body yalnız doğrulama ihtiyacı bulunan endpoint'lerde kullanılmalı ve loglanmamalıdır.
Signature Verification
Provider header'daki signature server-side secret ile yeniden hesaplanan değerle karşılaştırılır. Karşılaştırmada timing-safe yöntem kullanmak uygun olabilir. Timestamp içeren signature scheme replay attack riskini azaltabilir. Verification başarısızsa request business handler'a ulaşmamalıdır. Secret rotation sırasında birden fazla geçerli key kısa süre desteklenecekse bu davranış açık test edilmelidir.
Body Parser Sıralaması
Raw body capture parse işleminden önce veya parser'ın raw capture desteğiyle yapılmalıdır. Nest built-in rawBody desteği bu ihtiyacı framework seviyesinde kolaylaştırır. Global body parser'ı tamamen kapatmak rawBody convenience davranışını etkileyebilir. Custom parser registration adapter-specific olabilir. Migration sırasında Express ve Fastify body size default'ları birbirinden farklı olabileceği için explicit limit tercih edilebilir.
Request'in Controller'a Gönderilmesi
Signature doğrulaması başarılı olduğunda controller parse edilmiş DTO ile business flow'u başlatabilir. Raw buffer yalnız verification ihtiyacı için tutulur. Controller provider payload'ını doğrudan domain model kabul etmek yerine mapping layer kullanabilir. Idempotency key webhook'un tekrar gönderilmesi durumunda duplicate işlem riskini azaltır. Validation ve signature verification ayrı güvenlik kontrolleridir ve ikisi de uygulanmalıdır.
NestJS Interceptor Nedir?
NestJS interceptor request handler'ın çalışmasını çevreleyebilen ve response stream üzerinde işlem yapabilen injectable bileşendir. Aspect-Oriented Programming yaklaşımından esinlenen bu yapı, birçok controller'da tekrar eden cross-cutting davranışları merkezi hâle getirir. Handler'dan önce timing veya trace setup yapılabilir, handler sonrasında response dönüştürülebilir veya metrics tamamlanabilir. Interceptor `next.handle()` yerine başka Observable döndürerek execution'ı tamamen değiştirebilir. Bu esneklik güçlüdür, fakat business logic interceptor içine taşındığında controller davranışının görünürlüğü hızla azalır.
Aspect-Oriented Programming Mantığı
AOP farklı business method'larında tekrar eden concern'leri ortak bir katmanda ele alma fikrine dayanır. Logging, metrics ve caching bu tür concern'lere örnektir. Interceptor controller method'unun içine kod eklemeden önce ve sonra davranış ekler. Bu yapı feature kodunun cross-cutting concern'lerden ayrılmasını sağlar. Interceptor'ın route-specific business kararlarını saklayan bir mekanizmaya dönüşmemesi AOP kullanımının sınırını korur.
Handler Öncesinde Logic
`intercept()` method'unda `next.handle()` çağrısından önceki kod handler öncesinde çalışır. Başlangıç timestamp'i, span attribute veya metadata okuma burada yapılabilir. Database'den business data getirip controller'a gizli context eklemek çoğu zaman uygun değildir. Pre-handler logic mümkün olduğunca hızlı olmalıdır. Global interceptor olduğu durumda her endpoint bu maliyeti ödeyecektir.
Handler Sonrasında Logic
`next.handle()` Observable döndürdüğü için response tarafı RxJS pipeline ile işlenebilir. `map()` response'u dönüştürür, `tap()` side effect çalıştırır ve `finalize()` completion sonrası cleanup yapar. Handler exception fırlatırsa Observable error channel üzerinden ilerler. Bu özellik duration veya error metric'lerini tek noktadan toplamayı kolaylaştırır. Response manipulation standard API contract için güçlü olsa da stream ve binary response'lar ayrıca korunmalıdır.
Response Stream'i Manipüle Etmek
Handler'ın return değeri Observable stream içinde taşınır. Interceptor bu stream'e operator ekleyerek data shape veya error behavior'ını değiştirebilir. Generic response envelope, null normalization veya serialization yapılabilir. Response stream'i tamamen buffer'layan ağır işlemler büyük payload'larda memory maliyeti oluşturur. File veya streaming endpoint'ler global dönüşüm interceptor'ından gerektiğinde hariç tutulmalıdır.
Handler Execution'ını Override Etmek
Interceptor `next.handle()` çağırmak zorunda değildir. Cache hit olduğunda `of(cachedValue)` dönerek controller ve service execution'ı atlanabilir. Bu davranış bilinçli kullanılmazsa route'un neden çalışmadığını anlamak zorlaşır. Override koşulu explicit metadata veya açık cache policy ile tanımlanmalıdır. Business rule “bu endpoint çalışmasın” gibi kararlar interceptor yerine daha uygun application veya authorization katmanında değerlendirilmelidir.
NestInterceptor Interface Nasıl Çalışır?
`NestInterceptor` interface'i interceptor class'larının `intercept()` method'u uygulamasını bekler. Method `ExecutionContext` ve `CallHandler` olmak üzere iki temel araç alır. Context hangi execution'ın gerçekleştiğini, CallHandler ise gerçek handler stream'ini temsil eder. Interceptor synchronous veya asynchronous hazırlık yapabilse de sonunda Observable veya uygun async yapı döndürür. Interface sayesinde reusable interceptor'lar farklı controller ve transport context'lerinde aynı framework modelini takip eder.
intercept()
`intercept()` bütün interceptor davranışının giriş noktasıdır. Context'ten route metadata okunabilir ve `next.handle()` ile downstream execution başlatılır. Method içinde error handling veya response transformation RxJS pipe ile tanımlanabilir. `intercept()` çok büyük bir orchestration method'una dönüşürse concern'leri ayrı interceptor'lara bölmek daha sağlıklıdır. Unit test hem handler çağrılan hem handler skip edilen branch'leri kapsamalıdır.
ExecutionContext
`ExecutionContext` controller class ve handler reference gibi execution bilgilerini sunar. Ayrıca HTTP, RPC veya WebSocket context'e geçiş yapılabilir. GraphQL kullanımında `GqlExecutionContext.create()` ile GraphQL-specific context elde edilir. Bu abstraction reusable transport-aware interceptor yazmayı mümkün kılar. Interceptor yalnız HTTP varsayımı yapıyorsa `getType()` ile bunu explicit kontrol etmek hata riskini azaltır.
CallHandler
`CallHandler` handler execution'a erişim sağlayan `handle()` method'una sahiptir. Interceptor wrapper zincirinde sonraki interceptor veya final route handler bu çağrıyla ilerler. Handle çağrısı olmadan downstream execution yapılmaz. Dönen Observable response ve exception stream'ini temsil eder. CallHandler unit testlerde `of(value)` veya `throwError()` döndüren basit mock ile rahatça simüle edilebilir.
next.handle()
`next.handle()` interceptor zincirinin iç katmanına geçiş yapar. Request tarafında çağrıldığı anda controller'a doğru execution devam eder. Response tamamlandığında Observable geri dönerek outer interceptor pipeline'larını ters sırada çözer. Bu wrapper yapısı timing ve tracing için doğal try-finally benzeri model sağlar. Çağrının unutulması controller'ın sessizce çalışmamasına neden olabileceği için testler önemlidir.
Observable Response Stream
Nest interceptor response handling RxJS Observable üzerinden modellenir. Handler Promise döndürse bile framework interceptor tarafında stream abstraction sunar. Bu sayede mapping, error recovery ve finalization declarative operator zinciriyle yapılabilir. Observable bilgisinin yetersiz olması interceptor kodunda nested subscription gibi kötü pattern'lere yol açabilir. Interceptor içinde manuel `subscribe()` yerine stream'i framework'e geri döndürmek temel kuraldır.
ExecutionContext Nedir?
`ExecutionContext` Nest'in çalışacak handler hakkında runtime bilgi sunduğu framework abstraction'dır. Guard ve interceptor gibi bileşenler aynı API üzerinden controller class ve method reference alabilir. HTTP dışında RPC, WebSocket ve GraphQL senaryolarında transport context'e göre farklı argument yapıları bulunur. Bu nedenle generic interceptor yazarken yalnız Express request varsayımına bağlanmak reusable yapıyı sınırlar. Context metadata ve transport bilgisini birleştirmek route-specific fakat framework uyumlu davranış oluşturmanın temelidir.
Controller Bilgisi
`context.getClass()` çalışmak üzere olan controller class reference'ını verir. Controller adını logging veya metric attribute olarak kullanabilirsiniz. Metadata decorator controller seviyesinde tanımlandıysa Reflector ile bu class üzerinde okunabilir. Class name minification veya build davranışından etkilenebileceği için public API contract olarak kullanılmamalıdır. Observability label olarak kullanıldığında cardinality sabit ve düşük olduğu için raw URL'den daha güvenlidir.
Handler Bilgisi
`context.getHandler()` route veya resolver method reference'ını verir. Method üzerine eklenen custom metadata Reflector ile okunabilir. Handler name metrics ve structured loglarda route template yerine destekleyici bilgi olabilir. Method reference business authorization metadata'sı için guard katmanında da kullanılır. Reusable decorator ve interceptor kombinasyonları bu API üzerine kurulabilir.
HTTP Context
`context.switchToHttp()` HTTP request ve response argument'larına erişim sağlar. Express veya Fastify adapter kullanıldığında gerçek request type farklıdır. Adapter-agnostic interceptor yalnız ortak HTTP alanlarına güvenmelidir. Native response object'e doğrudan müdahale etmek framework response pipeline'ını etkileyebilir. Transport type kontrol edilmeden HTTP context çağırmak microservice veya WebSocket kullanımını bozar.
RPC Context
`switchToRpc()` microservice message data ve context bilgilerine erişmek için kullanılır. Kafka, RabbitMQ veya NATS transport farklı native context ayrıntıları taşıyabilir. Transport-agnostic interceptor ortak execution davranışını yönetirken transport-specific branch'ler ayrı helper'a taşınabilir. HTTP status gibi kavramlar RPC context'e doğrudan uygulanmamalıdır. Metrics label transport türünü belirterek aynı interceptor'ın farklı ortamlardaki davranışını ayırabilir.
WebSocket Context
`switchToWs()` WebSocket client ve message data erişimi sağlar. HTTP request-response modelinden farklı olarak uzun ömürlü connection ve message bazlı execution vardır. Request duration yerine message handler duration ölçmek daha anlamlıdır. Authentication handshake veya gateway context'te yapılırken message authorization ayrıca guard ile kontrol edilebilir. Interceptor response mapping WebSocket acknowledgement modeline göre test edilmelidir.
Reusable Transport-Agnostic Interceptor
Reusable interceptor önce `context.getType()` ile transport türünü belirleyebilir. Ortak timing ve error metric logic transporttan bağımsız kalabilir. Request path veya HTTP status gibi alanlar yalnız HTTP branch'inde eklenir. GraphQL type için ayrı context dönüşümü gerekebilir. Böyle bir tasarım copy-paste interceptor'lar yerine tek ortak observability katmanı sağlayabilir.
CallHandler ve next.handle() Ne İşe Yarar?
`CallHandler` interceptor'ın downstream execution'a geçiş yaptığı kapıdır. `next.handle()` çağrısı sonraki interceptor veya final handler'ın Observable'ını döndürür. Interceptor bu Observable'ı olduğu gibi döndürebilir veya operator'lerle zenginleştirebilir. Cache veya başka override senaryosunda bu çağrı yapılmayarak downstream tamamen atlanabilir. Bu davranışı anlamak interceptor'ın neden middleware'den farklı bir wrapper olduğunun en açık göstergesidir.
Handler'ı Çalıştırmak
Interceptor normal akışta `return next.handle()` döndürdüğünde handler execution gerçekleşir. Birden fazla interceptor varsa çağrı içteki interceptor'a devam eder. Final controller method'u tamamlandığında değer Observable üzerinden dışa doğru geri gelir. Pre ve post logic bu nedenle aynı interceptor içinde tanımlanabilir. Handler çağrısı conditional yapılacaksa condition unit ve E2E test ile açıkça doğrulanmalıdır.
next.handle() Çağrılmazsa Ne Olur?
Downstream interceptor ve controller handler çalışmaz. Interceptor kendi Observable'ını döndürüyorsa client bu sonucu alabilir. Hiç geçerli stream döndürülmezse runtime behavior hatalı olur. Cache interceptor'ın hit branch'i bilinçli bir kullanım örneğidir. Yanlışlıkla unutulan call ise application feature'ını sessiz biçimde devre dışı bırakabilir.
Observable Döndürülmesi
Interceptor Nest'e response stream'i temsil eden Observable döndürür. `of(value)` sabit veya cached response için kullanılabilir. `throwError()` error stream üretir. Asenkron source varsa RxJS veya Promise conversion dikkatle yapılmalıdır. Framework subscription'ı yönettiği için interceptor içinde manuel subscribe etmek çoğu zaman yanlış tasarımdır.
Response Stream Üzerinde İşlem Yapma
`next.handle().pipe(...)` ile response data ve completion lifecycle işlenebilir. `map` değeri değiştirirken `tap` onu değiştirmeden side effect çalıştırabilir. `catchError` belirli exception'ı başka exception'a çevirebilir. `finalize` success veya failure fark etmeksizin cleanup sağlar. Operator seçimi concern'in response value'yu değiştirip değiştirmediğine göre yapılmalıdır.
NestJS Interceptor ve RxJS İlişkisi
NestJS interceptor'ın gücünün önemli bölümü RxJS stream modelinden gelir. Handler sonucu Observable içinde temsil edildiği için response transformation, timeout ve error mapping declarative operator zinciriyle yapılabilir. RxJS'i yalnız syntax olarak kullanmak operator semantics bilinmediğinde beklenmeyen davranış oluşturabilir. Özellikle `tap`, `finalize` ve `catchError` birbirinden farklı amaçlara sahiptir. NestJS interceptor ile response transform logging ve hata yönetimi nasıl yapılır sorusunu doğru yanıtlamak için temel Observable akışını anlamak gerekir.
Observable Nedir?
Observable zaman içinde bir veya daha fazla değer, error veya completion signal üretebilen stream abstraction'dır. HTTP handler çoğu zaman tek response değeri üretse bile interceptor API aynı reactive modeli kullanır. Promise'den farklı olarak operator composition daha geniş lifecycle kontrolü sunar. Observable lazy olduğu için subscription gerçekleşmeden source execution başlamayabilir. Nest framework subscription yönetimini kendisi yaptığı için custom interceptor stream'i geri döndürmelidir.
pipe()
`pipe()` Observable üzerine operator zinciri ekler. Birden fazla operator soldan sağa deklaratif biçimde uygulanır. Response transform ve timing logic ayrı operator'lerde tutulabilir. Çok uzun pipe zinciri interceptor'ın birden fazla sorumluluk taşıdığını gösterebilir. Operator sırası error ve transformation davranışını etkilediği için testlerle korunmalıdır.
map()
`map()` stream'deki başarılı değeri başka bir değere dönüştürür. `{ data, meta }` response envelope bunun klasik kullanım alanıdır. `map()` side effect logging için tercih edilmemelidir çünkü amacı value transformation'dır. Büyük object üzerinde recursive mapping CPU maliyeti oluşturabilir. Generic type'lar response shape'in compile-time anlaşılmasını kolaylaştırır.
tap()
`tap()` stream değerini değiştirmeden side effect çalıştırır. Başarılı response sonrasında log veya metric increment için kullanılabilir. Error callback de tanımlanabilse de success-error sonrası ortak cleanup gerekiyorsa `finalize()` daha uygun olabilir. Tap içinde ağır synchronous I/O response latency'yi etkiler. Logging backend'e network call yapılıyorsa buffered veya async logger yaklaşımı tercih edilmelidir.
finalize()
`finalize()` Observable complete veya error ile sonlandığında çalışır. Duration metric'i hem success hem failure için tamamlamak açısından kullanışlıdır. Response value'ya erişmek yerine lifecycle kapanışına odaklanır. Resource cleanup veya active request gauge decrement işlemleri için uygundur. Finalize callback içindeki error'ın original stream behavior'ını bozmamasına dikkat edilmelidir.
catchError()
`catchError()` error stream'i gözlemleyebilir ve yeni Observable döndürebilir. Domain-specific exception'ı Nest HTTP exception'a dönüştürmek bazı interceptor senaryolarında mümkündür. Global error response contract için exception filter çoğu zaman daha doğru sorumluluk sınırıdır. Error'ı loglayıp aynı error'ı tekrar fırlatırken duplicate logging riskine dikkat edilmelidir. Catch branch'in hangi exception'ları değiştirdiği açık ve dar tutulmalıdır.
timeout()
RxJS `timeout()` response stream belirli süre içinde tamamlanmadığında TimeoutError üretebilir. Interceptor bu hatayı `RequestTimeoutException` gibi HTTP exception'a çevirebilir. Bu application-level request timeout database query timeout ile aynı şey değildir. Streaming endpoint'lerde uzun açık stream normal davranış olabileceği için generic timeout uygun olmayabilir. Timeout budget upstream proxy ve downstream dependency süreleriyle uyumlu tasarlanmalıdır.
map(), tap() ve finalize() Arasındaki Fark
Bu üç operator aynı response akışında kullanılsa da amaçları farklıdır. `map()` başarılı değeri değiştirir, `tap()` değeri değiştirmeden belirli event'lerde side effect yapar ve `finalize()` stream sonlandığında ortak cleanup çalıştırır. Response envelope oluşturmak için `map`, success logging için `tap`, her durumda duration tamamlamak için `finalize` daha doğal seçimdir. Yanlış operator seçimi kodun niyetini belirsizleştirir. Ekip içinde interceptor operator standardı belirlemek code review süresini azaltabilir.
map() ile Response Dönüştürme
Handler `User[]` döndürürken interceptor `{ data: User[] }` üretebilir. `map()` original value'yu alıp yeni shape döndürür. Generic response interface TypeScript tarafında contract'ı belirginleştirir. File veya StreamableFile gibi response'lar global mapping'den hariç tutulabilir. Dönüşüm business data üretmemeli, yalnız presentation contract düzenlemelidir.
tap() ile Side Effect
Tap response'u değiştirmeden logging veya metric güncellemesi için kullanılır. Başarılı completion callback response data'ya erişebilir. Sensitive response body'yi loglamak privacy ve cost riski oluşturur. Timing için yalnız success tap kullanılırsa error request'lerin duration metriği kaybolabilir. Bu nedenle duration cleanup `finalize` ile daha güvenilir olabilir.
finalize() ile Success ve Error Sonrası Logic
Finalize stream hangi sonuçla biterse bitsin çalışır. Active request counter decrement veya duration histogram observe işlemi için uygundur. Response body'yi transform etmez. Error type ayrımı gerekiyorsa catchError veya tap error callback ile ek state tutulabilir. Finalize cleanup kodu kısa ve exception-safe olmalıdır.
Logging İçin Hangisi Kullanılmalı?
Request başlangıç logu `next.handle()` öncesinde yazılabilir. Success event bilgisi gerekiyorsa `tap()` kullanılabilir. Success ve failure fark etmeksizin completion duration loglamak için `finalize()` daha sağlamdır. Error detail exception filter veya centralized error logger tarafından zaten yazılıyorsa interceptor duplicate stack trace üretmemelidir. Logging mimarisi tek bir operator seçmekten çok event ownership belirlemeye dayanmalıdır.
Custom Logging Interceptor Nasıl Yazılır?
Custom logging interceptor handler metadata'sını ve execution süresini tek bir structured event içinde birleştirebilir. Request başlangıcında timestamp alınır, `ExecutionContext` üzerinden controller ve handler adı belirlenir. Response tamamlandığında elapsed time hesaplanır ve status ile error durumu eklenir. Correlation ID AsyncLocalStorage üzerinden alınabilir. Production logger stdout veya remote sink'e ağır synchronous serialization yapmamalıdır.
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { finalize, Observable } from 'rxjs';
@Injectable()
export class TimingInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<unknown> {
const startedAt = performance.now();
const controller = context.getClass().name;
const handler = context.getHandler().name;
return next.handle().pipe(
finalize(() => {
const durationMs = performance.now() - startedAt;
console.log({
controller,
handler,
durationMs,
});
}),
);
}
}
Request Başlangıç Zamanı
Interceptor `next.handle()` çağrısından hemen önce monotonic timer başlatabilir. `performance.now()` elapsed duration için wall-clock Date kullanımından daha uygundur. Timing interceptor guard execution süresini kapsamaz çünkü guard daha önce çalışır. Full incoming request duration gerekliyse middleware timestamp'i ile interceptor verisi birleştirilebilir. Hangi latency'nin ölçüldüğü metric isminde açıkça belirtilmelidir.
Controller ve Handler Adı
`ExecutionContext.getClass()` ve `getHandler()` low-cardinality route identity sağlar. Raw URL yerine bu metadata metrics label için daha güvenlidir. Dynamic URL parametreleri metric cardinality'yi patlatmaz. Method isimleri refactor sırasında değişebileceği için dashboard query'leri version control ile güncellenmelidir. Public endpoint isimlendirme standardı observability okunabilirliğini artırır.
Response Süresi
Start timestamp ile finalize arasındaki fark handler execution ve inner interceptor süresini kapsar. Database ve downstream service latency bu değere dahildir. Outer middleware'deki total request duration ile fark guard, pipe veya framework overhead konusunda fikir verebilir. P50 yanında P95 ve P99 duration izlenmelidir. Tek request logundan performans kararı vermek yerine histogram metric kullanılmalıdır.
HTTP Status
HTTP response status adapter response nesnesinden okunabilir. Error filter status'ı daha sonra değiştirebiliyorsa interceptor'ın gördüğü timing noktası önemlidir. Successful route default 200 veya POST 201 olabilir. Status code metric label düşük cardinality sunduğu için güvenlidir. GraphQL veya RPC interceptor aynı logic'i kullanıyorsa HTTP status yalnız HTTP branch'inde eklenmelidir.
Error Durumu
Interceptor error stream'i `tap({ error })` veya `catchError` üzerinden gözlemleyebilir. Exception stack'i birden fazla katmanda loglamak duplicate ve yüksek log maliyeti yaratır. Interceptor yalnız error class ve timing metric yazarken global filter detaylı log ownership alabilir. Expected validation veya authorization errors warning yerine normal structured event olarak sınıflandırılabilir. Error severity business impact ve status family'ye göre belirlenmelidir.
Structured Log
Structured log text template yerine JSON field'ları taşır. Request ID, controller, handler, durationMs ve status gibi alanlar query edilebilir. Password, authorization header veya request body varsayılan log field'ı olmamalıdır. Logger schema service'ler arasında ortak olduğunda merkezi dashboard ve alert oluşturmak kolaylaşır. High-cardinality kullanıcı ID gibi alanlar loglarda kontrollü kullanılabilir fakat metrics label olarak taşınmamalıdır.
Middleware Logging mi Interceptor Logging mi?
Logging için tek bir katmanın her ihtiyacı karşılaması gerekmez. Middleware request'i en erken noktada gördüğü için guard tarafından reddedilen veya controller'a ulaşmayan request'leri de kaydedebilir. Interceptor ise controller ve handler metadata'sını bildiği için application execution logunda daha zengin context sunar. Full request duration için middleware, handler duration için interceptor ölçümü birlikte tutulabilir. İki katman aynı structured event'i tekrar üretmek yerine birbirini tamamlayan farklı metric ve log sorumluluklarına sahip olmalıdır.
Middleware Ne Kadar Erken Çalışır?
Middleware guards, pipes ve interceptor'lardan önce çalışır. Bu nedenle malformed veya unauthorized request bile middleware access logunda görünebilir. Incoming timestamp total server latency ölçümü için iyi başlangıç noktasıdır. Route handler henüz resolve edilmediği için method metadata bilgisi sınırlıdır. Reverse proxy access log ile application middleware log arasında correlation ID paylaşmak troubleshooting'i kolaylaştırır.
Interceptor Handler Metadata'sını Bilir
Interceptor execution context controller ve method'u bildiği için log event'ini semantic endpoint identity ile etiketleyebilir. `/users/123` yerine `UsersController.findOne` gibi düşük cardinality alan kullanılabilir. Custom route metadata business operation adı sağlayabilir. Bu bilgi middleware'de doğal olarak bulunmaz. Metrics ve trace span naming için interceptor bu nedenle daha güçlüdür.
Request Duration Ölçümü
Middleware response `finish` event'ine kadar geçen süreyi ölçerek full HTTP lifecycle'a yakın değer sağlayabilir. Interceptor duration guard aşamasını kapsamaz. İki değer arasındaki fark security veya validation overhead'i incelemek için kullanılabilir. Tek metric seçilecekse kullanıcıya en yakın total duration genellikle önemlidir. Application handler optimization için interceptor duration ayrıca değerlidir.
Rejected Guard Request'leri
Guard request'i reddettiğinde inner interceptor hiç çalışmayabilir. Yalnız interceptor logging kullanılırsa bu request'ler handler execution logunda görünmez. Middleware access log giriş ve final status bilgisiyle bunları kaydedebilir. Security audit için unauthorized request visibility önemlidir. Guard da authorization decision için ayrı security event üretebilir.
İki Katmanın Birlikte Kullanılması
Middleware correlation ID ve total HTTP timing sağlar, interceptor semantic handler timing ve metric üretir. Aynı request ID iki event'i birleştirir. Duplicate request body veya header logging yapılmamalıdır. Ortak logger schema her katmanın eventType alanını farklı tutabilir. Bu model production observability'de daha fazla sinyal sunarken sorumluluk sınırlarını korur.
NestJS'te Response Transformation
Response transformation controller'ların sürekli aynı envelope veya serialization kodunu tekrar etmesini önleyebilir. Interceptor handler dönüş değerini `map()` ile standard API response formatına dönüştürebilir. Pagination metadata veya null normalization gibi presentation-level ihtiyaçlar merkezi olarak uygulanabilir. Global transformation tasarlanırken file, stream ve manually managed response endpoint'leri düşünülmelidir. API contract client'larla versioned olduğu için interceptor değişikliği sıradan refactor değil contract değişikliği kabul edilmelidir.
Standard API Response
API bütün başarılı response'larda ortak `{ data }` biçimi kullanabilir. Bu contract frontend client generic parsing'i kolaylaştırabilir. Ancak gereksiz envelope her API için şart değildir. Error response contract başarılı response'dan ayrı tasarlanabilir. Response interceptor business service'in return type'ını network contract'a dönüştüren presentation layer olarak görülmelidir.
{ data, meta } Envelope
Liste endpoint'leri data yanında pagination veya query metadata taşıyabilir. `{ data, meta }` biçimi bu ihtiyacı açık şekilde karşılar. Tek entity endpoint'inde boş meta üretmek gereksiz olabilir. Generic interceptor response'un zaten envelope olup olmadığını metadata veya wrapper type üzerinden anlayabilir. Double wrapping production'da sık görülen global interceptor hatalarından biridir.
Pagination Metadata
Pagination response total, page, limit veya cursor bilgisini taşıyabilir. Database service pagination sonucunu presentation-friendly object olarak döndürebilir. Interceptor yalnız ortak envelope oluşturabilir. Pagination hesabını interceptor içinde database query ile yapmak doğru değildir. Cursor gibi opaque token'lar loglara gereksiz yazılmamalıdır.
Null Değerleri Dönüştürmek
Global null-to-empty-string dönüşümü teknik olarak interceptor ile yapılabilir. Ancak null semantiği API contract açısından anlamlı olabilir ve generic dönüşüm veri anlamını bozabilir. Frontend boş string ile null değerini farklı yorumlayabilir. Böyle bir policy kullanılacaksa schema ve API documentation ile açıkça tanımlanmalıdır. Deep recursive transformation büyük payload'larda CPU maliyeti yaratabilir.
Response Field Mapping
Entity alanlarını public response DTO'ya map etmek hassas data sızıntısını azaltır. ClassSerializerInterceptor veya explicit mapping kullanılabilir. Domain entity'yi doğrudan controller response olarak döndürmek zamanla internal field'ların istemeden dışarı çıkmasına neden olabilir. Interceptor generic serialization sağlayabilir fakat business-specific field mapping dedicated mapper'da daha açık olabilir. Security review response DTO contract'larını düzenli kontrol etmelidir.
Generic Response Interceptor Nasıl Yazılır?
Generic response interceptor TypeScript generic type'ları kullanarak handler output ile API envelope arasındaki ilişkiyi ifade edebilir. `NestInterceptor>` gibi type signature code completion ve review sırasında niyeti açıklar. `map()` ile successful value yeni response object içine yerleştirilir. Pagination gibi özel sonuçlar ayrı discriminated type üzerinden ele alınabilir. Global interceptor her response'u koşulsuz wrap etmek yerine skip metadata ve special response türlerini desteklemelidir.
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { map, Observable } from 'rxjs';
interface ApiResponse<T> {
data: T;
}
@Injectable()
export class ResponseInterceptor<T>
implements NestInterceptor<T, ApiResponse<T>> {
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<ApiResponse<T>> {
return next.handle().pipe(
map((data: T) => ({ data })),
);
}
}
Generic Type Kullanımı
Generic type handler output'un keyfi `any` olmasını azaltır. Controller `UserDto` döndürüyorsa interceptor output'u `ApiResponse` olarak ifade edilebilir. Runtime serialization yine TypeScript type sisteminden bağımsızdır. Generic type security validation yerine geçmez. Type contract OpenAPI veya başka schema generation ile uyumlu tutulmalıdır.
map() Operator
Map successful response value'yu envelope içine alır. Error stream map callback'e successful value gibi gelmez. Bu nedenle exception contract ayrı filter tarafından yönetilebilir. Null veya undefined response için policy explicit tanımlanmalıdır. Heavy recursive mapping generic interceptor içinde yapılmamalıdır.
Success Response Formatı
Success response örneğin `data`, `meta` ve requestId alanlarını taşıyabilir. RequestId AsyncLocalStorage'dan okunabilir. Her endpoint için gereksiz timestamp veya path field eklemek payload boyutunu artırır. Contract frontend ihtiyaçları üzerinden sade tutulmalıdır. API version değiştiğinde interceptor ve documentation birlikte güncellenmelidir.
Pagination ile Birlikte Kullanım
Pagination result özel wrapper type ile interceptor'a taşınabilir. Interceptor result'ın paginated olduğunu type guard veya metadata ile anlayabilir. Data ve meta ayrı response alanlarına yerleştirilir. Controller'ın zaten envelope döndürmesiyle interceptor'ın tekrar wrap etmesi önlenmelidir. Integration test normal, paginated ve empty response örneklerini kapsamalıdır.
@Res() Kullanımı Interceptor'ları Nasıl Etkiler?
Nest controller standard response strategy kullanıldığında handler return değeri framework response pipeline'ından geçer. Native `@Res()` kullanarak response doğrudan gönderildiğinde bazı Nest özellikleri bu değeri artık yönetemez. Response mapping interceptor'larının çalışmaması bunun en bilinen sonucudur. `@Res({ passthrough: true })` native response üzerinde header veya cookie işlemi yaparken dönüş değerini Nest'e bırakmaya yardımcı olur. Controller seviyesinde manual response gönderimi platform bağımlılığını ve test maliyetini artırdığı için yalnız gerçekten gerekli durumlarda kullanılmalıdır.
Nest Standard Response Pipeline
Standard pipeline controller'ın return ettiği object'i framework'e teslim eder. Interceptor transformation ve ClassSerializerInterceptor gibi özellikler bu akışta çalışabilir. Nest status code ve response body yazımını underlying adapter'a kendisi uygular. Controller unit test için native response mock gerektirmez. Bu nedenle standart strategy çoğu REST endpoint için önerilen varsayımdır.
Library-Specific Response Strategy
`@Res()` native Express Response veya Fastify Reply nesnesine erişim verir. Controller `res.status(...).json(...)` benzeri platform-specific çağrılar yapar. Framework artık handler return value üzerinden aynı standard response yönetimini yapamaz. Adapter değişimi bu controller kodunu doğrudan etkiler. Special streaming veya low-level response kontrolü dışında kullanım dikkatle sınırlandırılmalıdır.
Response Mapping'in Çalışmadığı Durumlar
Handler native response'u kendi gönderdiğinde interceptor `map()` ile değiştirilen return value network response'a uygulanmayabilir. CacheInterceptor da native response kullanan HTTP route'larda çalışmayabilir. Developer interceptor'ın bug'lı olduğunu düşünürken asıl problem response strategy olabilir. Global response contract kullanan projelerde lint veya code review `@Res()` kullanımını sınırlandırabilir. E2E test global envelope'un bütün normal endpoint'lerde korunduğunu doğrulamalıdır.
@Res({ passthrough: true }) Yaklaşımı
Passthrough seçeneği response object üzerinde cookie veya header ayarlamaya izin verirken body yönetimini Nest'e bırakır. Controller normal return value döndürebilir. Böylece interceptor ve serialization pipeline korunabilir. Yine de native response type controller signature'a adapter bağımlılığı ekler. Yalnız framework decorator ile çözülemeyen ihtiyaç varsa kullanılması daha temizdir.
Controller'da Manuel Response Göndermenin Maliyeti
Manual response gönderimi controller testinde response mock gerektirir. Express'ten Fastify'a geçiş maliyeti artar. Global interceptor ve decorator contract'ları bypass edilebilir. Aynı status veya header logic controller'lar arasında tekrar etmeye başlayabilir. Bu nedenle low-level control kazanımının architecture maliyeti bilinçli kabul edilmelidir.
ClassSerializerInterceptor Nedir?
`ClassSerializerInterceptor` Nest'in `class-transformer` tabanlı response serialization çözümüdür. Handler tarafından döndürülen class instance'larını plain object'e dönüştürürken decorator tabanlı expose veya exclude kurallarını uygulayabilir. Password gibi hassas alanların response'dan çıkarılması için yararlıdır. DTO veya entity instance kullanımı önemlidir, çünkü plain object davranışı configuration'a göre farklı olabilir. Serialization presentation concern olduğu için service business logic'inden ayrı tutulması daha uygundur.
DTO Serialization
Response DTO hangi alanların dış API contract'a açılacağını tanımlar. Class serializer DTO instance üzerinde dönüşüm kurallarını uygular. Internal entity alanlarının otomatik olarak client'a sızması riski azalır. Validation DTO ile response DTO aynı class olmak zorunda değildir. Explicit request ve response modelleri büyük projelerde değişiklik etkisini sınırlar.
Hassas Alanları Gizleme
Password hash, internal token veya private metadata response contract'ta bulunmamalıdır. `@Exclude()` benzeri class-transformer decorator'ları merkezi protection sağlayabilir. Bunun tek güvenlik kontrolü olduğu varsayılmamalıdır. Response DTO mapping de sensitive field allowlist yaklaşımı sunar. Security test serialized output'ta yasaklı alanların bulunmadığını doğrulamalıdır.
class-transformer
ClassSerializerInterceptor altında `class-transformer` conversion mekanizmasını kullanır. Decorator configuration runtime transformation davranışını etkiler. Büyük ve iç içe object graph'larında serialization maliyeti ölçülmelidir. Library version upgrade breaking behavior açısından test edilmelidir. TypeScript type declaration runtime property exposure garantisi sağlamaz.
Response DTO Kullanımı
Response DTO public API schema'sını domain entity'den ayırır. Mapping explicit constructor veya mapper service ile yapılabilir. DTO üzerinde serializer decorator kullanılarak presentation policy merkezi tutulur. API versioning farklı DTO sürümleriyle daha rahat yönetilebilir. Controller return type'ın DTO olması documentation ve type safety açısından faydalıdır.
Manuel Mapping ile Karşılaştırma
Manuel mapping hangi alanların çıktığını çok açık gösterir. ClassSerializerInterceptor tekrar eden conversion kodunu azaltır. Çok hassas security contract'larında explicit allowlist mapping tercih edilebilir. Genel CRUD response'larında declarative serialization daha hızlı geliştirme sağlayabilir. Ekip project riskine göre tek standardı veya kontrollü hibrit yaklaşımı seçmelidir.
Cache Interceptor Nasıl Kullanılır?
Nest cache interceptor GET response'larını otomatik cache etmek için kullanılabilir. Cache key varsayılan olarak request URL gibi request bilgilerine dayanabilir ve gerektiğinde custom tracking yapılabilir. TTL cache entry yaşam süresini sınırlar. Global cache interceptor bütün uygun route'ları etkileyebileceği için authentication ve tenant isolation dikkatle tasarlanmalıdır. Cache invalidation çoğu sistemde cache yazmaktan daha zor olduğu için business data freshness requirement baştan belirlenmelidir.
Response Caching
Cache hit durumunda interceptor handler execution'ı atlayıp cached response döndürebilir. Bu database ve service latency'sini azaltır. Dynamic user-specific response ortak key ile cache edilirse data leak oluşabilir. Yalnız GET olması response'un otomatik olarak cache-safe olduğu anlamına gelmez. Authentication, tenant ve query parametreleri key tasarımına dahil edilmelidir.
Cache Key
Cache key aynı response'u paylaşabilecek request'leri doğru biçimde ayırmalıdır. Raw authorization header key olarak kullanılmamalıdır. Tenant veya user identity gerekiyorsa güvenli internal identifier kullanılabilir. Query parametre order normalize edilmezse aynı logical request birden fazla entry oluşturabilir. Key schema version değişikliği cache migration'ı kolaylaştırabilir.
TTL
TTL cached response'un ne kadar süre geçerli kalacağını belirler. Çok uzun TTL stale data riskini artırır, çok kısa TTL cache hit oranını düşürür. Data değişim sıklığı ve business freshness requirement birlikte değerlendirilmelidir. Randomized TTL büyük cache stampede riskini azaltabilir. Critical data için explicit invalidation TTL'den daha önemli olabilir.
Global Cache Interceptor
`APP_INTERCEPTOR` ile CacheInterceptor global provider olarak kaydedilebilir. Bütün GET route'ların cache davranışı tekrar gözden geçirilmelidir. Health veya user-specific endpoint'ler default tracking ile istenmeyen cache sonucu üretebilir. Custom metadata route'u cache dışına çıkarabilir. Global convenience security review maliyetini ortadan kaldırmaz.
Route Bazlı Cache
Yalnız gerçekten cache faydası olan endpoint'lere interceptor bağlamak daha kontrollü yaklaşım olabilir. Expensive read endpoint veya public reference data iyi adaydır. Route metadata TTL veya key behavior belirleyebilir. Write endpoint caching yapılmamalıdır. Performance benchmark cache layer'ın gerçek hit ratio ve latency katkısını ölçmelidir.
Cache Invalidation Problemi
Data update edildiğinde eski cache entry'nin ne zaman silineceği belirlenmelidir. TTL tek başına kullanıcıya kabul edilemez stale data gösterebilir. Write service ilgili key'leri invalidate edebilir veya event-driven invalidation kullanılabilir. Çok geniş wildcard invalidation cache verimliliğini düşürür. Cache architecture veri ownership ve update pattern üzerinden tasarlanmalıdır.
Timeout Interceptor Nasıl Çalışır?
Timeout interceptor handler Observable'ına RxJS `timeout()` operator ekleyerek belirli süreden uzun execution'ı hata ile sonlandırabilir. TimeoutError uygun Nest `RequestTimeoutException` tipine dönüştürülebilir. Bu süre database veya downstream HTTP timeout'unu otomatik olarak iptal etmez. Dependency call'lara ayrıca cancellation veya daha kısa timeout budget aktarılmalıdır. Streaming veya long-poll endpoint'lere global kısa timeout uygulamak normal çalışma biçimini bozabilir.
RxJS timeout()
`timeout()` source Observable belirlenen şartta değer üretmez veya tamamlanmazsa error oluşturabilir. HTTP handler için request deadline uygulamakta kullanılabilir. Operator davranışı RxJS sürümüne göre option biçimleri taşıyabilir. Timeout değerinin magic number olarak interceptor içine gömülmesi yerine config veya metadata kullanmak daha esnektir. Route-specific uzun işlemler explicit override edebilmelidir.
Request Timeout
Request timeout kullanıcıya belirli sürede yanıt üretilemediğinde uygulamanın vazgeçme sınırıdır. Reverse proxy veya load balancer timeout bundan daha kısa ise application exception client'a ulaşmayabilir. Total budget guard, pipe, handler ve downstream call süreleriyle birlikte düşünülmelidir. Timeout yalnız average latency değil P99 behavior üzerinden belirlenmelidir. Çok uzun süre resource'ları işgal eden request'ler concurrency sorununa dönüşebilir.
TimeoutException
Nest `RequestTimeoutException` HTTP 408 benzeri application response üretmek için kullanılabilir. Upstream proxy timeout'u farklı status döndürebilir. Error contract filter tarafından standard body formatına dönüştürülebilir. Timeout'un retryable olup olmadığı endpoint semantics'e bağlıdır. Client'a internal dependency ayrıntısı verilmemelidir.
Database Timeout ile Farkı
Interceptor timeout application response stream'ini sınırlar, database query kendi driver'ında çalışmaya devam edebilir. Database driver query timeout veya cancellation destekliyorsa daha kısa dependency deadline verilmelidir. Aksi hâlde client request bitse bile database resource tüketimi sürebilir. Bu durum load altında cascading failure oluşturabilir. Deadline propagation production backend tasarımında interceptor'dan daha geniş bir konudur.
Streaming Response'larda Sınırlamalar
Streaming endpoint uzun süre açık kalmayı amaçlar. İlk chunk sonrası bile stream completion çok geç olabilir. Generic timeout stream'i normal davranış sırasında kesebilir. Route metadata ile timeout interceptor skip edilebilir. Streaming için idle timeout ve total lifetime farklı kavramlar olarak tasarlanmalıdır.
Exception Mapping Interceptor ile Yapılmalı mı?
Interceptor `catchError()` ile exception'ı başka exception'a dönüştürebilir, fakat bu her error mapping işinin interceptor'a ait olduğu anlamına gelmez. Global HTTP error contract exception filter için daha doğal sorumluluktur. Interceptor handler-specific bir dependency error'ını domain veya application exception'a çevirmekte kullanılabilir. Filter final transport representation'ı üretir. Bu ayrım exception translation ile error response formatting görevlerini birbirinden ayırır.
catchError()
`catchError()` Observable error kanalını yakalar. Error loglanabilir, başka exception'a dönüştürülebilir veya kontrollü fallback stream döndürülebilir. Her error'ı generic 500'e çevirmek original context'i kaybettirir. Unexpected error stack monitoring'e ulaşmalıdır. Catch branch mümkün olduğunca dar exception type'larını hedeflemelidir.
Exception Transform
External SDK'nin özel error type'ı application-specific exception'a çevrilebilir. Controller veya service bu SDK dependency'sine doğrudan bağlı kalmaz. Ancak bu mapping service adapter katmanında da daha uygun olabilir. Interceptor yalnız handler cross-cutting mapping gerçekten gerekiyorsa seçilmelidir. Error code ve retryability gibi semantic bilgiler transform sırasında kaybedilmemelidir.
Interceptor ve Exception Filter Farkı
Interceptor handler stream'i çevreler ve success ile error davranışını birlikte değiştirebilir. Exception filter exception oluştuğunda final response handling'e odaklanır. Filter HTTP, RPC veya GraphQL transport contract'ına göre farklı output üretebilir. Global response error schema filter'da daha açık görünür. Interceptor'ın her error'ı yutması filter'ın hiç devreye girmemesine neden olabilir.
Global Error Contract İçin Filter
API bütün hatalarda `code`, `message`, `requestId` ve optional details alanları kullanabilir. Global exception filter bu contract'ı tek yerde uygular. Validation ve authorization exception'ları da aynı output family içine alınabilir. Sensitive stack trace yalnız internal logda tutulmalıdır. Client message ile internal debug message ayrı alanlar olarak ele alınmalıdır.
Domain-Specific Error Mapping
Domain service business exception üretirse HTTP controller veya filter bunu uygun transport response'a çevirebilir. Böylece domain katmanı HTTP status bilmese de olur. Interceptor belirli decorator metadata'sıyla mapping yapmak için kullanılabilir, fakat bu pattern aşırı kullanılırsa error flow görünmez olur. Mapping table merkezi ve test edilebilir olmalıdır. Aynı domain error GraphQL veya RPC'de farklı representation alabilir.
Middleware vs Interceptor
Middleware ve interceptor arasındaki temel seçim execution zamanı ve gerekli context üzerinden yapılmalıdır. Middleware daha erken çalışır ve raw request-response nesnelerine doğrudan erişir. Interceptor controller metadata'sını bilir ve handler'ın return stream'ini çevreleyebilir. Dependency injection her iki yapıda da mümkün olsa da global registration biçimleri farklı sınırlar taşır. Nest.js Middleware ve Interceptor Kullanımı için en pratik kural, HTTP preprocessing işini middleware'e, handler-aware before-after davranışı interceptor'a yerleştirmektir.
Execution Time
Middleware request lifecycle'ın başında guard'lardan önce çalışır. Interceptor guard'lardan sonra ve pipe'lardan önce request yönünde devreye girer. Response yönünde interceptor handler tamamlandıktan sonra tekrar çalışır. Middleware response finish event dinleyebilir fakat Nest return value stream'ini doğal olarak görmez. Ölçmek veya değiştirmek istediğiniz aşama katman seçiminde belirleyicidir.
Request'e Erişim
Middleware request nesnesini doğrudan parametre olarak alır. Interceptor HTTP context'te `switchToHttp().getRequest()` üzerinden request'e erişebilir. Middleware request preprocessing için daha doğal noktadır. Interceptor request'e erişirken aynı zamanda handler metadata'sını da bilir. Request'e erişebilmek tek başına hangi katmanın doğru olduğunu belirlemez.
Response'a Erişim
Middleware native response object'e doğrudan erişebilir. Interceptor HTTP response object'e erişebilse de asıl gücü handler return Observable'ındadır. Response header eklemek her iki yerde de mümkündür. Return data mapping interceptor'da daha doğal ve framework uyumludur. Native response'a doğrudan body yazmak standard Nest pipeline'ını bypass edebilir.
Handler Metadata
Middleware hangi controller method'un çalışacağını doğal olarak bilmez. Interceptor `ExecutionContext` üzerinden class ve handler bilgisine erişir. Custom decorators ve Reflector bu metadata'yı reusable policy hâline getirebilir. Route-specific caching veya logging option'ı interceptor için uygundur. Middleware'de route metadata taklit etmek framework abstraction'ını yeniden yazmaya dönüşebilir.
Route Result
Middleware controller return değerini Observable olarak alamaz. Interceptor `next.handle()` üzerinden result stream'e erişir. Response envelope, serialization veya result caching bu nedenle interceptor ile daha temiz uygulanır. Middleware yalnız native response'u monkey patch ederek benzer davranış üretmeye çalışmamalıdır. Framework'ün sunduğu doğru lifecycle hook kullanılmalıdır.
Dependency Injection
Module üzerinden tanımlanan class middleware DI kullanabilir. `app.use()` ile global functional middleware module DI context dışında çalışır. Interceptor normal provider olduğunda constructor injection kullanabilir. `useGlobalInterceptors(new ...)` ile manuel instance oluşturmak DI'yi sınırlar. Global injectable interceptor için `APP_INTERCEPTOR` tercih edilmelidir.
RxJS Desteği
Interceptor response handling doğrudan RxJS Observable modeline dayanır. Middleware normal Express veya Fastify middleware callback modelindedir. Timeout, response map ve error stream manipulation interceptor'da operator'lerle ifade edilir. Middleware içinde RxJS kullanmak teknik olarak mümkün olsa bile framework tarafından gerekli değildir. Concern stream-aware ise interceptor genellikle daha doğal seçimdir.
Kullanım Senaryoları
Request ID, cookie parse ve raw HTTP normalization middleware için uygundur. Timing, response transformation, caching ve serialization interceptor'a daha uygundur. Authorization guard, DTO validation pipe ve error contract filter tarafından yönetilmelidir. Aynı concern iki katmana dağılırsa ownership documentation ile açık olmalıdır. Production pipeline'da her cross-cutting concern'in tek ana sahibi bulunması debugging süresini azaltır.
Middleware vs Guard
Middleware ve guard security alanında en sık karıştırılan iki katmandır. Middleware authentication credential'ını parse edip request'e identity context ekleyebilir, fakat route metadata'sını bilmediği için authorization kararında sınırlıdır. Guard hangi handler'ın çalışacağını bildiğinden roles ve permissions için daha doğal noktadır. Passport gibi Nest authentication modelleri çoğu zaman guard yaklaşımını doğrudan kullanır. Security logic ayrımı “kimlik bilgisi nasıl çıkarılır?” ve “bu handler'a girme hakkı var mı?” soruları üzerinden yapılabilir.
Authentication Parsing
Authorization header veya session cookie request'in erken aşamasında parse edilebilir. Token doğrulama maliyeti yüksekse middleware ile bütün route'larda yapmak public endpoint'lerde gereksiz overhead yaratabilir. Guard route metadata üzerinden authentication requirement belirleyebilir. Shared auth library parsing ve validation logic'i guard veya strategy içinde tekrar kullanılabilir. Authentication result request veya context'e güvenli identity olarak eklenir.
Authorization Decision
Authorization “bu identity bu operation'ı yapabilir mi?” sorusudur. Route permission metadata'sı bu kararın önemli girdisidir. Guard handler'ı bildiği için policy enforcement burada doğal olarak yapılır. Middleware path string üzerinden authorization taklit ederse route refactor güvenlik kuralını bozabilir. Guard failure standard Nest exception flow'a katılır.
Route Metadata
`SetMetadata` veya custom decorator route üzerine role ve permission bilgisi yazabilir. Guard Reflector kullanarak method ve controller seviyesindeki metadata'yı okuyabilir. Middleware aynı ExecutionContext'e sahip değildir. Bu nedenle declarative security model guard ile daha rahat kurulur. Metadata key'leri string magic value yerine typed decorator helper ile merkezi tutulmalıdır.
Roles ve Permissions
Basit uygulamada role kontrolü yeterli olabilir. Kurumsal projede resource ownership, tenant ve action permission birlikte değerlendirilebilir. Guard policy service'i inject ederek karar verebilir. Business object lookup gerekiyorsa authorization service ile data access maliyeti kontrollü tasarlanmalıdır. Permission sonucunun request boyunca tekrar kullanılabilmesi için context caching düşünülebilir.
Neden Authorization Guard'a Aittir?
Guard route execution öncesinde admission kararı vermek üzere tasarlanmıştır. Handler metadata bilgisi yetki politikasını declarative hâle getirir. Authorization başarısızsa pipes veya controller çalışmadan request durur. Middleware bu semantic lifecycle bilgisinden yoksundur. Bu yüzden path bazlı custom middleware authorization çoğu Nest projesinde gereksiz framework bypass'ı oluşturur.
Interceptor vs Guard
Guard ve interceptor ikisi de ExecutionContext görse de sorumlulukları farklıdır. Guard handler'a giriş izni verir veya reddeder. Interceptor izin verilmiş execution'ı before-after logic ile çevreler. Authentication veya authorization kararını interceptor'a koymak `next.handle()` çağrısı çevresinde security behavior'ı gizler. Timing veya response mapping'i guard'a koymak da admission concern'ini kirletir.
Guard'ın Admission Kararı
Guard `canActivate()` true veya false sonucu üzerinden route admission kontrol eder. Promise veya Observable da döndürebilir. False durumunda Nest handler'a ilerlemez. Authorization exception veya custom error üretilebilir. Bu sade contract security code review'ü kolaylaştırır.
Interceptor'ın Handler Wrapper Rolü
Interceptor `next.handle()` ile execution'ı çevreler. Route zaten guard kontrollerini geçmiş kabul edilir. Pre-handler timing ve post-handler mapping aynı wrapper içinde yapılabilir. Cache hit gibi özel durumda handler skip edilebilir. Bu skip security admission yerine performance veya cross-cutting davranış için kullanılmalıdır.
Authentication Nerede Yapılmalı?
Nest projelerinde authentication sıklıkla guard ve strategy katmanında yönetilir. Middleware yalnız generic credential extraction veya request context hazırlığı yapabilir. Authentication sonucu route access requirement ile birleşecekse guard doğru semantic noktadır. Global public-private route policy custom decorator ile tanımlanabilir. Authentication service token verification logic'i yeniden kullanılabilir provider olarak tutulmalıdır.
Authorization Nerede Yapılmalı?
Authorization guard veya dedicated policy guard tarafından yapılmalıdır. Handler metadata roles, permissions veya policies belirler. Interceptor authorization reddi için kullanılmamalıdır. Service katmanı kritik resource ownership kuralını ayrıca enforce edebilir. Defense-in-depth aynı business rule'ı dağınık biçimde üç katmanda tekrarlamak anlamına gelmez.
Interceptor vs Pipe
Interceptor handler'ın tamamını çevrelerken pipe belirli controller argument'ı üzerinde çalışır. DTO validation ve primitive conversion pipe'ın doğal görevidir. Response transformation veya timing ise interceptor concern'idir. İki yapı request lifecycle'da birbirine yakın görünse de farklı veri seviyelerinde çalışır. Input contract'ı handler wrapper içinde doğrulamaya çalışmak pipe abstraction'ının sunduğu test ve decorator avantajlarını kaybettirir.
Pipe Argument Bazında Çalışır
Pipe route parameter, query veya body argument'ına uygulanabilir. `ParseIntPipe` string route parametresini number'a dönüştürür. `ValidationPipe` DTO instance üzerinde validation çalıştırabilir. Argument-specific error message daha anlamlı olur. Interceptor bütün handler context'ini gördüğü için bu kadar dar input responsibility'ye sahip değildir.
Interceptor Handler Bazında Çalışır
Interceptor method execution'ın wrapper'ıdır. Birden fazla argument'ın her biriyle ayrı validation contract üzerinden ilgilenmez. Handler sonucunu transform edebilir. Controller metadata'sına bağlı caching veya metrics uygulayabilir. Input DTO validation interceptor içine taşındığında framework decorator ergonomisi kaybolur.
DTO Validation
DTO validation global veya route pipe ile yapılabilir. `class-validator` veya custom validation yaklaşımı input contract'ı tanımlar. Whitelist ve transform seçenekleri güvenlik davranışını etkiler. Business validation service katmanında ayrıca gerekebilir. Syntax validation ile domain rule aynı şey değildir.
Parametre Transformation
Route id string'den integer'a pipe ile çevrilebilir. Query enum değerleri doğrulanabilir. Controller method typed ve temiz value alır. Transformation başarısızsa handler çalışmaz. Interceptor bu işlemi yaparsa hangi argument'ın değiştirildiği koddan daha zor anlaşılır.
Input Validation Neden Pipe'a Aittir?
Pipe framework tarafından argument processing için özel olarak tasarlanmıştır. Decorator seviyesinde dar scope uygulanabilir. Validation exception standard Nest error flow'a doğal katılır. Test input değeri ve expected transformed output üzerinden basittir. Interceptor'ı input validation için kullanmak daha geniş bir lifecycle tool'u gereksiz yere yanlış role sokar.
Interceptor vs Exception Filter
Interceptor error stream'i görebilir, exception filter ise exception'ı final transport response'una dönüştürmek üzere tasarlanmıştır. Bu iki yetenek birbirine benzese de ownership farklıdır. Interceptor latency veya domain-specific transform sırasında error'ı gözlemleyebilir. Filter status code, error body ve request ID gibi final contract'ı üretebilir. Global error davranışını tek bir filter altında tutmak client contract'ını daha anlaşılır yapar.
Error Stream'i Observe Etmek
Interceptor `tap` error callback veya `catchError` ile handler error'ını görebilir. Metric increment veya timing için bu yeterlidir. Error stack'i tekrar loglamak duplicate kayıt oluşturabilir. Observable error tekrar throw edilirse outer interceptors ve filters çalışmaya devam eder. Error'ı swallow etmek bilinçli fallback dışında kaçınılmalıdır.
Exception'ı Response'a Map Etmek
Exception filter HTTP status ve response body üretmek için uygun katmandır. Domain error code user-friendly message'e dönüştürülebilir. Internal stack trace client'a verilmez. GraphQL veya RPC filter farklı response semantics kullanabilir. Transport mapping domain service'inden ayrıldığında reusable business code oluşur.
Global Error Contract
Global filter bütün REST endpoint'lerde ortak error envelope sağlayabilir. Correlation ID eklenerek support workflow kolaylaştırılır. Validation errors field list taşıyabilir. Unexpected exception generic public message döndürürken internal log detayını korur. Error schema OpenAPI ve frontend type generation ile senkron tutulmalıdır.
Ne Zaman Hangisi Kullanılmalı?
Metrics veya handler wrapper içinde error transform gerekiyorsa interceptor değerlendirilebilir. Final HTTP error response üretmek için filter seçilmelidir. Business rule error'ı service veya domain katmanında ortaya çıkmalıdır. Bir exception'ın aynı anda interceptor ve filter tarafından farklı biçimde değiştirilmesi debugging'i zorlaştırır. Error ownership architecture guideline içinde yazılı olmalıdır.
Interceptor Scope Türleri
Interceptor method, controller veya global scope'ta uygulanabilir. Dar scope feature-specific behavior'ı görünür tutarken global scope bütün endpoint'lerde ortak standardı uygular. Scope genişledikçe binary response, streaming, GraphQL veya internal endpoint gibi istisnalar artabilir. Global interceptor'ların skip metadata ve transport awareness desteği düşünülmelidir. Scope seçimi yalnız kod tekrarını değil future maintenance ve debugging maliyetini de etkiler.
Method-Scoped
Method scope yalnız tek route handler'a interceptor bağlar. Özel timeout veya cache policy için idealdir. Davranış route decorator yanında görüldüğü için discoverability yüksektir. Çok sayıda route aynı interceptor'ı tekrar kullanmaya başlarsa controller veya global scope değerlendirilebilir. Method-specific behavior unit ve E2E testte kolay izole edilir.
Controller-Scoped
Controller class üzerine interceptor bağlandığında bütün handler'lar etkilenir. Feature-specific response mapping veya metrics policy için uygun olabilir. Bir route istisnası gerekiyorsa skip metadata kullanılabilir. Controller büyüdükçe scope etkisi review sırasında unutulabilir. Controller-level E2E test farklı route tiplerini kapsamalıdır.
Global-Scoped
Global interceptor bütün controller ve route handler'lara uygulanır. Standard serialization, tracing veya timing için güçlüdür. Her request ek overhead ödediği için logic hafif tutulmalıdır. HTTP dışı transport varsa context type kontrolü gerekir. File, stream ve health endpoint istisnaları açık policy ile yönetilmelidir.
Scope Seçiminin Bakım Maliyeti
Dar scope duplicate decorator yaratabilir, global scope ise görünmeyen davranış oluşturabilir. En doğru seviye concern'in gerçekten ne kadar ortak olduğuna bağlıdır. Global standard yalnız yüzde seksen endpoint'e uyuyorsa sürekli skip decorator birikmesi kötü işarettir. Feature module bazlı provider composition alternatif olabilir. Scope decision architecture review sırasında kullanım oranı ve istisna sayısıyla değerlendirilmelidir.
@UseInterceptors() Nasıl Kullanılır?
`@UseInterceptors()` decorator interceptor'ı controller veya route seviyesine bağlar. Class reference kullanmak Nest'in DI container üzerinden instance yönetmesine izin verir. `new Interceptor()` ile manuel instance vermek dependency injection ihtiyacını sınırlayabilir. Birden fazla interceptor aynı decorator içinde sıralı olarak tanımlanabilir. Decorator order ve response FİLO davranışı integration test ile doğrulanmalıdır.
Route Method
Decorator route method üzerinde kullanıldığında yalnız o handler etkilenir. Route-specific timeout, cache veya special transformation için uygundur. Handler'ın yanında görünmesi davranışın discoverability'sini artırır. Çok fazla decorator route signature'ını kalabalıklaştırıyorsa composite custom decorator düşünülebilir. Security concern guard decorator'ıyla karıştırılmamalıdır.
Controller
Controller class seviyesindeki interceptor bütün method'larda çalışır. Feature-specific logging enrichment veya response serializer uygulanabilir. Child route'ların farklı response type'ları varsa global mapping dikkatli olmalıdır. Route-level başka interceptor içe doğru eklenir. Scope ordering test edilmeden varsayılmamalıdır.
Class Referansı Kullanmak
`@UseInterceptors(LoggingInterceptor)` şeklindeki class reference Nest tarafından resolve edilebilir. Constructor dependency'leri normal DI yoluyla sağlanır. Interceptor provider olarak erişilebilir olmalıdır. Test module dependency'leri kolay mock edebilir. Genellikle manual instance kullanımından daha iyi production varsayımıdır.
Instance Kullanmak
`@UseInterceptors(new LoggingInterceptor())` teknik olarak kullanılabilir. Dependency gerektirmeyen küçük interceptor için çalışır. Ancak framework DI ve lifecycle yönetimi bypass edilir. Config veya logger eklemek gerektiğinde refactor gerekir. Kurumsal codebase'lerde class reference standardı daha sürdürülebilir olabilir.
Dependency Injection Etkisi
DI kullanılan interceptor logger, config veya reflector gibi provider'lara erişebilir. Class reference veya `APP_INTERCEPTOR` provider registration bu capability'yi korur. Manual `new` dependency graph'i Nest dışında kurar. Testlerde provider override etmek zorlaşır. Global interceptor architecture'ında DI ihtiyacı baştan düşünülmelidir.
Global Interceptor Nasıl Tanımlanır?
Global interceptor iki temel biçimde tanımlanabilir: application instance üzerinde `useGlobalInterceptors()` veya module provider olarak `APP_INTERCEPTOR`. İlki bootstrap sırasında manuel instance registration için uygundur. Ancak module dışından oluşturulan instance normal dependency injection avantajlarını kaybedebilir. `APP_INTERCEPTOR` provider yaklaşımı global scope'u korurken DI container içinde kalır. Production logging, tracing veya serializer gibi dependency kullanan interceptor'larda provider yaklaşımı genellikle daha esnektir.
useGlobalInterceptors()
`app.useGlobalInterceptors(new LoggingInterceptor())` application-wide registration yapar. Basit dependency-free interceptor için çalışır. Instance bootstrap code içinde oluşturulur. Module-scoped dependency injection doğrudan kullanılamaz. Test bootstrap setup global interceptor'ı ayrıca register etmeyi unutmamalıdır.
Global Scope
Global interceptor bütün route handler'larda çalışır. Global olduğu için controller decorator'larında görünmez. Architecture documentation veya common module registration davranışı keşfedilebilir kılmalıdır. Performance overhead her request'i etkiler. Global interceptor listesi mümkün olduğunca kısa tutulmalıdır.
Dependency Injection Sorunu
Bootstrap sırasında `new` ile oluşturulan interceptor Nest DI container dışında kalır. Constructor logger veya config provider alamaz. Manuel service locator yaklaşımı eklemek gereksiz coupling oluşturur. Bu durumda `APP_INTERCEPTOR` kullanılmalıdır. Framework provider lifecycle'ı test ve override işlemlerini de kolaylaştırır.
APP_INTERCEPTOR
`APP_INTERCEPTOR` special provider token interceptor'ı global olarak kaydeder. Provider herhangi bir module içinde tanımlansa da etkisi application global scope olur. `useClass`, `useExisting` veya `useFactory` custom provider pattern'leri kullanılabilir. Dependency injection normal şekilde çalışır. Global concern'ler için Nest'in önerdiği güçlü registration modellerinden biridir.
APP_INTERCEPTOR Ne İşe Yarar?
`APP_INTERCEPTOR` Nest core tarafından global interceptor provider registration için sunulan token'dır. Interceptor DI container içinde oluşturulduğu için constructor dependency'leri ve provider lifecycle özellikleri korunur. Aynı token birden fazla provider için kullanılabilir ve global interceptor chain oluşturulabilir. Registration module location'ı interceptor'ın semantic ownership'ini göstermelidir. Birden fazla global interceptor order'ı application startup testinde explicit doğrulanmalıdır.
Module Provider Olarak Registration
Provider array içinde `{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor }` tanımlanabilir. Module load edildiğinde interceptor global olarak uygulanır. Common observability module bu provider'ı export etmek zorunda olmayabilir. Aynı module duplicate import behavior dikkatle incelenmelidir. Dynamic module composition global provider'ın birden fazla kez register edilmesini önlemelidir.
Dependency Injection
Interceptor constructor'ına logger, Reflector veya config service inject edilebilir. Provider test module içinde override edilebilir. Request-scoped dependency eklemek bütün interceptor lifecycle maliyetini değiştirebilir ve dikkat gerektirir. Singleton dependency çoğu global concern için yeterlidir. DI graph startup circular dependency üretmeyecek kadar sade tutulmalıdır.
Global Scope
`APP_INTERCEPTOR` hangi module'da tanımlanırsa tanımlansın global scope oluşturur. Developer yalnız ilgili module route'larının etkileneceğini varsaymamalıdır. Provider ismi ve documentation bu davranışı açık belirtmelidir. Feature-specific interceptor global token ile register edilmemelidir. E2E test unrelated controller'ın da interceptor'dan geçtiğini doğrulayabilir.
useClass
`useClass` token için Nest'in yeni interceptor instance oluşturmasını sağlar. Basit global registration için en yaygın yöntemdir. Class dependency'leri container tarafından resolve edilir. Aynı class başka token altında da provider ise ayrı instance oluşturulabileceği unutulmamalıdır. Shared singleton instance önemliyse `useExisting` düşünülebilir.
useExisting
`useExisting` önceden kayıtlı provider instance'ını APP_INTERCEPTOR token için tekrar kullanabilir. Böylece aynı interceptor'ın duplicate instance'ı oluşmaz. Provider önce module içinde normal token ile tanımlanmalıdır. Testlerde aynı instance state veya spy izlemek kolaylaşabilir. Stateful global interceptor kullanımı yine dikkatli tasarlanmalıdır.
useFactory
`useFactory` configuration'a göre interceptor instance veya implementation oluşturmak için kullanılabilir. Factory dependency'leri `inject` array üzerinden alır. Environment bazlı observability configuration bu modelden faydalanabilir. Factory içinde network I/O veya ağır initialization yapılmamalıdır. Conditional provider behavior mümkün olduğunca startup'ta deterministik olmalıdır.
Birden Fazla Interceptor'ın Çalışma Sırası
Birden fazla interceptor request yönünde dıştan içe, response yönünde ise içten dışa çözülür. Global interceptor'lar controller ve route interceptor'larından önce request'i çevrelemeye başlar. Controller interceptor ardından, route interceptor ise handler'a en yakın katmanda çalışabilir. Observable completion response tarafında ters sıra oluşturur. Bu first-in-last-out davranışı logging, transaction benzeri wrapper veya response mapping sırasını doğrudan etkiler.
Global Interceptor
Global interceptor request execution'ın dış wrapper'larından biridir. Bütün route'larda ortak tracing veya timing başlangıcı yapabilir. Inner controller interceptor response'u değiştirdikten sonra global response mapping devreye girebilir. Order aynı scope içinde registration sırasına bağlı olabilir. Contract-critical order E2E testle korunmalıdır.
Controller Interceptor
Controller interceptor global layer'ın içinde çalışır. Feature-wide metadata veya response behavior uygulayabilir. Route interceptor varsa onun dışında wrapper olur. Response yönünde route interceptor'dan sonra kendi operator'leri çalışır. Bu sıranın business data transformation üzerinde double mapping yaratmaması gerekir.
Route Interceptor
Route interceptor handler'a en yakın declarative layer olabilir. Handler-specific cache veya timing behavior burada uygulanabilir. Response ilk olarak bu interceptor'ın operator zincirinden geçer. Sonra controller ve global interceptor'lara doğru geri çıkar. Route-level mapping global envelope ile uyumlu tasarlanmalıdır.
Request Yönü
Request yönünde registration hierarchy dıştan içe ilerler. Pre-handler loglar bu sırada oluşur. Outer interceptor timestamp'i daha erken aldığı için daha geniş süre ölçer. Inner interceptor'ın error'ı outer layer tarafından görülebilir. Trace span parent-child tasarımı bu wrapper modeline uyarlanabilir.
Response Yönü
Response handler'dan çıktıktan sonra interceptor stack tersine çözülür. En içteki map ilk transformation'ı yapar. Outer interceptor değiştirilmiş değeri görür. Error da aynı Observable stack üzerinden dışa ilerler. Global response envelope'ın inner feature mapping sonrasında çalışması bilinçli contract olarak belirlenmelidir.
First-In-Last-Out Mantığı
İlk giren interceptor response aşamasında son çıkar. Bu davranış nested function call veya middleware wrapper modeline benzer. `A before`, `B before`, `handler`, `B after`, `A after` sırası basit mental modeldir. Ekip lifecycle testinde marker'larla bu sırayı doğrulayabilir. Framework upgrade sonrası kritik order assumption'ları regression test sayesinde korunur.
Conditional Interceptor Nasıl Tasarlanır?
Global interceptor bazı route'larda farklı davranmalı veya tamamen atlanmalıysa custom metadata kullanılabilir. Decorator route veya controller üzerine flag ekler, interceptor Reflector ile bu değeri okur. Bu yaklaşım path string karşılaştırmaktan daha güvenilir ve refactor-friendly'dir. Skip decorator sayısı aşırı büyüyorsa global scope kararının yanlış olabileceği değerlendirilmelidir. Conditional logic tek interceptor içinde onlarca business branch'e dönüşmemelidir.
Custom Metadata
Custom metadata handler veya controller üzerinde framework-independent policy bilgisi taşır. Örneğin `SkipResponseWrap` veya `CachePolicy` decorator oluşturulabilir. String key collision riskini azaltmak için symbol veya constant kullanılabilir. Metadata public business data taşımamalıdır. Reflector method ve class hierarchy'den değeri okuyabilir.
SetMetadata()
`SetMetadata()` custom decorator oluşturmanın temel araçlarından biridir. Route üzerine boolean veya configuration object yazılabilir. Raw `SetMetadata` her controller'da tekrar edilmek yerine semantic custom decorator ile sarılmalıdır. Böylece key adı merkezi olur. Type-safe helper decorator refactor güvenliğini artırır.
Reflector
`Reflector` execution context metadata'sını okumayı kolaylaştırır. `getAllAndOverride` controller ve method seviyesindeki değerlerin önceliğini yönetebilir. Global interceptor DI üzerinden Reflector alabilir. Metadata bulunmazsa güvenli default behavior uygulanmalıdır. Reflector business service lookup aracı değil framework metadata katmanıdır.
Route Bazlı Interceptor Davranışı
Timeout süresi veya response wrapping route metadata'sıyla ayarlanabilir. Controller default value, route override sağlayabilir. Bu declarative yapı magic path listelerinden daha okunabilirdir. Metadata configuration çok geniş object'e dönüşürse interceptor'ın sorumluluğu büyümüş olabilir. E2E test default ve override route'ları ayrı doğrulamalıdır.
Interceptor Skip Decorator
Özel `@SkipResponseTransform()` decorator global transformation'ı belirli route'ta devre dışı bırakabilir. File download veya streaming endpoint buna örnektir. Skip decorator security concern'leri bypass etmek için kolay bir araca dönüşmemelidir. Usage repository search ile düzenli gözden geçirilebilir. Global standard'ın sürekli skip edilmesi architecture smell olarak değerlendirilmelidir.
Express ve Fastify ile Middleware Farkları
Nest platform abstraction sunsa da middleware underlying HTTP adapter davranışına daha yakındır. Express ve Fastify request-response type'ları, plugin sistemi ve middleware integration ayrıntıları farklıdır. Nest documentation bu nedenle middleware signature'larının adapter'a göre değişebileceğini açıkça belirtir. Interceptor, guard ve pipe gibi Nest enhancer'ları daha platform-agnostic tasarlanabilir. Adapter değişimi planlanıyorsa middleware kodu en fazla incelenmesi gereken katmanlardan biridir.
NestJS HTTP Adapter
Nest HTTP adapter Express veya Fastify gibi platformları ortak framework API altında çalıştırır. Default `@nestjs/platform-express` paketidir. Fastify farklı adapter ile explicit seçilir. Controller standard response strategy adapter değişiminden daha az etkilenir. Native request-response API kullanan kod adapter coupling oluşturur.
Express Middleware
Express middleware klasik `(req, res, next)` callback modelini kullanır. Nest default middleware davranışı bu modele oldukça yakındır. Çok geniş npm middleware ekosistemi Express ile uyumludur. Express 5 path matching ve query parsing davranışlarında migration farkları getirmiştir. Üçüncü taraf middleware'in Express 5 support durumu upgrade öncesi doğrulanmalıdır.
Fastify Middleware
Fastify native olarak hook ve plugin modelini öne çıkarır, Nest adapter middleware compatibility katmanı sunabilir. Express için yazılmış her middleware Fastify'da doğrudan aynı şekilde çalışmayabilir. Request ve reply type'ları farklıdır. Performance-sensitive uygulamada native Fastify hook kullanımı bazı concern'ler için daha uygun olabilir. Framework-independent service logic bu adapter ayrıntılarından ayrı tutulmalıdır.
Request/Response Type Farkları
Express `Request` ve `Response`, Fastify `FastifyRequest` ve `FastifyReply` kullanır. TypeScript code platform-specific method çağrılarını compile seviyesinde gösterir. Custom middleware package iki adapter destekleyecekse ortak abstraction veya ayrı adapter implementation gerektirebilir. `@Res()` kullanımının migration maliyeti burada netleşir. Standard Nest decorators platform değişiminde daha az coupling sunar.
Adapter-Agnostic Kod Yazmak
Business service hiçbir Express veya Fastify type'ı bilmemelidir. Interceptor yalnız method ve handler metadata'sıyla çalışabiliyorsa native request API'den kaçınabilir. HTTP-specific information gerektiğinde küçük adapter helper yazılabilir. Platform migration test suite aynı endpoint contract'ını iki adapter üzerinde doğrulayabilir. Adapter independence hedef değilse bile coupling'in bilinçli olması future migration maliyetini hesaplamayı sağlar.
NestJS 11, Express 5 ve Fastify 5 Güncellemeleri
NestJS 11 backend projelerinde runtime ve HTTP adapter güncellemeleri açısından önemli değişiklikler içerir. Node.js 20 veya daha güncel desteklenen runtime gerekir. Express 5 NestJS 11'in varsayılan Express sürümüdür ve route matching ile query parsing davranışlarında eski projeleri etkileyebilecek değişiklikler taşır. `@nestjs/platform-fastify` v11 Fastify 5 desteği sunar. Migration package update ile sınırlı görülmemeli, middleware registration, wildcard paths, query parser, CORS ve integration testleri birlikte kontrol edilmelidir.
Express 5 Varsayılan Adapter
Nest varsayılan HTTP platformu Express olmaya devam eder ve NestJS 11 ile Express 5'e geçiş yapılmıştır. Express 5 uzun süredir kullanılan bazı route syntax davranışlarını değiştirmiştir. Üçüncü taraf Express middleware compatibility kontrol edilmelidir. Native response veya request type kullanan kodlar regression test gerektirir. Standard Nest abstraction kullanan controller'lar çoğu durumda daha az değişiklik görür.
Node.js 20+ Gereksinimi
NestJS 11 Node.js 20 veya daha yeni desteklenen sürüm gerektirir. Node 16 ve 18 desteği kaldırılmıştır. Container base image ve CI runtime birlikte güncellenmelidir. Native dependency veya APM agent compatibility ayrıca kontrol edilir. Production ile developer local runtime farklı sürümde bırakılmamalıdır.
Named Wildcards
Express 5 wildcard parameter'ın isimlendirilmesini bekler. `*splat` ve root dahil `{*splat}` güncel örneklerdir. Nest bazı eski pattern'leri compatibility için dönüştürse de yeni code standardı named wildcard olmalıdır. Middleware ve route definitions repository genelinde taranmalıdır. Root path davranışı E2E testte explicit örnekle korunmalıdır.
Middleware Registration Order
NestJS 11'de global module middleware'lerinin diğer module middleware'lerinden önce çalışması daha tutarlı hâle getirilmiştir. Eski proje implicit topological ordering'e güveniyorsa davranış değişebilir. Request context veya logger order'ı bu nedenle test edilmelidir. Middleware marker E2E testi upgrade riskini azaltır. Registration sırasına business correctness bağlamak yine mümkün olduğunca önlenmelidir.
Migration Sırasında Kontrol Edilmesi Gerekenler
Node runtime, Express wildcard syntax ve query parser ilk kontrol alanlarıdır. Fastify kullanan projeler Fastify 5 migration notlarını ayrıca incelemelidir. Middleware registration order, CORS methods ve third-party package support test edilmelidir. E2E suite route matching, error contract ve response interceptor behavior'ını kapsamalıdır. Canary deployment production traffic altında gözden kaçan adapter farklarını erken gösterebilir.
GraphQL'de Interceptor Kullanımı
Nest guards, interceptors, filters ve pipes gibi enhancer yapılarını GraphQL resolver'larında da kullanabilir. Ancak GraphQL execution context HTTP REST context'ten farklı argument set'i taşır. `GqlExecutionContext` generic ExecutionContext'i resolver context'e dönüştürür. Top-level query ve mutation enhancer davranışı field resolver behavior'ından farklı olabilir. Çok sayıda field resolver'da enhancer çalıştırmak performance maliyeti üretebileceği için configuration bilinçli yapılmalıdır.
HTTP Context'ten Farkı
GraphQL resolver `root`, `args`, `context` ve `info` gibi argument'larla çalışır. Native HTTP response üzerine status/body yazmak GraphQL execution modeline uymaz. Request identity çoğu zaman GraphQL context içindeki request üzerinden alınır. Error response GraphQL spec ve server integration'a göre şekillenir. HTTP için yazılan interceptor context assumption'ları kontrol edilmeden GraphQL'e taşınmamalıdır.
ExecutionContext Type
GraphQL context type generic Nest context'ten `GqlExecutionContext.create()` ile elde edilebilir. `getArgs()`, `getContext()` ve benzeri API'ler resolver verilerine erişir. `context.getType()` GraphQL-specific generic type ile değerlendirilebilir. Reusable interceptor transport branch'i burada uygulayabilir. TypeScript helper GraphQL package dependency'sini yalnız ilgili integration module içinde tutabilir.
Resolver Interceptor
`@UseInterceptors()` query veya mutation resolver method'unda kullanılabilir. Timing, audit veya response-related concern resolver execution'ı çevreleyebilir. Field resolver enhancer behavior configuration'a bağlıdır. Binlerce field execution'da global heavy interceptor maliyeti büyüyebilir. GraphQL N+1 problemine interceptor cache ile yüzeysel çözüm aramak yerine data loader veya query design değerlendirilmelidir.
Logging
Operation name ve resolver handler metadata loglarda kullanılabilir. Raw GraphQL query sensitive field veya yüksek cardinality taşıyabilir. Variables password veya PII içerebilir ve varsayılan loglanmamalıdır. Correlation ID HTTP context'ten GraphQL execution'a taşınabilir. Error logging GraphQL server'ın kendi error hook'larıyla duplicate olmayacak şekilde tasarlanmalıdır.
Metrics
Resolver name düşük cardinality metric label olabilir. Field resolver metrics yüksek volume oluşturabileceği için selective instrumentation gerekir. Operation duration top-level query veya mutation seviyesinde daha kullanışlı olabilir. Raw query string label olarak kullanılmamalıdır. OpenTelemetry span hierarchy GraphQL request ve resolver execution'ı ayırabilir.
WebSocket Interceptor Kullanımı
Nest interceptor modeli WebSocket gateway handler'larında da kullanılabilir. ExecutionContext WebSocket client ve message data erişimi sunar. HTTP request-response yerine uzun ömürlü connection üzerinde message bazlı execution bulunur. Timing ve logging message handler seviyesinde ölçülmelidir. HTTP-specific status veya header logic transport-agnostic interceptor içinde koşulsuz kullanılmamalıdır.
Gateway-Scoped Interceptor
Gateway class üzerine interceptor bağlandığında message handler'ların tamamı etkilenebilir. Gateway-wide logging veya metrics için uygundur. Connection handshake davranışı ile message execution birbirinden ayrılmalıdır. Her message body'yi loglamak yüksek volume ve privacy riski yaratabilir. Scope test farklı event handler'larını kapsamalıdır.
Message Handler Interceptor
Tek `@SubscribeMessage` handler üzerine interceptor bağlanabilir. Expensive event için özel timeout veya metric uygulanabilir. Handler result acknowledgement veya emitted event biçimine göre transform edilebilir. Client disconnect durumları error metric'te ayrı sınıflandırılmalıdır. HTTP response interceptor'ını doğrudan kopyalamak doğru olmayabilir.
HTTP Interceptor ile Benzerlik
ExecutionContext ve CallHandler interface aynı temel modeli korur. `next.handle()` response Observable sunar. Before-after timing logic büyük ölçüde yeniden kullanılabilir. Fark native context ve response semantics'tedir. Generic interceptor ortak bölümü paylaşırken transport adapter helper kullanabilir.
WebSocket Context
`switchToWs()` client ve incoming data erişimi sağlar. Client identity handshake veya gateway authentication sonucunda tutulabilir. Message metadata request header kavramından farklıdır. Disconnect sonrası AsyncLocalStorage context cleanup behavior test edilmelidir. Connection ID metric label olarak yüksek cardinality oluşturabileceği için Prometheus'ta kullanılmamalıdır.
NestJS Microservices Interceptor Kullanımı
Nest microservice transport'larında interceptor message handler execution'ını çevreleyebilir. `@MessagePattern` handler HTTP controller'a benzer framework lifecycle enhancer'larından yararlanabilir. Kafka, RabbitMQ ve NATS context bilgileri farklı native metadata taşır. Transport-agnostic interceptor ortak timing ve tracing concern'lerini paylaşabilir. Retry, acknowledgement ve dead-letter behavior transport seviyesinde ayrıca düşünülmelidir.
@MessagePattern
`@MessagePattern` incoming RPC veya event message'ı Nest handler'a bağlar. Interceptor bu handler'ın execution context'ini bilir. Handler name metric ve trace için kullanılabilir. Message body sensitive data içeriyorsa loglanmamalıdır. Pattern name düşük cardinality observability attribute olarak değerlidir.
Kafka
Kafka context topic, partition, offset ve headers gibi metadata taşıyabilir. Correlation veya trace context header üzerinden propagate edilebilir. Message processing duration interceptor ile ölçülebilir. Offset commit veya retry behavior interceptor completion'ıyla birebir aynı olmayabilir. Exactly-once veya idempotency business service seviyesinde ayrıca tasarlanmalıdır.
RabbitMQ
RabbitMQ message property ve channel context transport-specific bilgilerdir. Interceptor shared metrics üretirken acknowledgement logic'i message handler veya transport config ile koordine edilmelidir. Redelivery count loglarda useful olabilir. Raw payload varsayılan log alanı olmamalıdır. Consumer concurrency performance benchmark ile belirlenmelidir.
NATS
NATS request-reply veya pub-sub pattern farklı message lifecycle davranışları sunar. Subject metadata operation identity olarak kullanılabilir. Interceptor timing ve trace correlation sağlayabilir. Timeout client ve server tarafında ayrı budget gerektirir. High-frequency event'lerde global heavy logging ciddi overhead oluşturabilir.
RPC Context
`switchToRpc()` data ve native context erişimi sağlar. Transport-specific type cast yalnız ilgili branch içinde yapılmalıdır. HTTP request object varsayımı generic interceptor'ı bozar. Error mapping RPC exception semantics'e uygun olmalıdır. Same interceptor HTTP ve RPC destekliyorsa tests iki context için ayrı yazılmalıdır.
Transport-Agnostic Interceptor
Timing, controller-handler identity ve basic error count transporttan bağımsız olabilir. Request URL veya HTTP status gibi alanlar abstraction'ın içine gömülmemelidir. Adapter helper context türüne göre semantic attribute üretir. Böylece observability standardı bütün backend transport'larında aynı kalır. Transport-specific performance concern ayrı module içinde yönetilebilir.
NestJS Observability'de Middleware ve Interceptor
Observability request'in sisteme girişini, handler execution'ını ve error sonucunu aynı correlation zincirinde görmeyi amaçlar. Middleware correlation ID ve incoming request timing için erken bir hook sağlar. Interceptor controller metadata, handler duration ve response outcome bilgisi ekleyebilir. OpenTelemetry trace context AsyncLocalStorage veya instrumentation üzerinden bütün service call'lara yayılabilir. Logging, metrics ve tracing birbirinin kopyası değil farklı soru türlerine cevap veren tamamlayıcı sinyaller olarak tasarlanmalıdır.
Correlation ID
Correlation ID request'i log kayıtları arasında bulmayı sağlar. Middleware external veya generated ID'yi context'e koyabilir. Interceptor aynı değeri handler loguna ekler. Downstream call'larda propagate edilirse distributed request takip edilebilir. Metric label olarak kullanılması cardinality nedeniyle uygun değildir.
Structured Logging
Structured logger event type, route, duration ve requestId gibi field'ları ayrı saklar. Text parsing ihtiyacı azalır. Request body varsayılan olarak loglanmamalıdır. Log level expected 4xx ve unexpected 5xx hatalarda farklı olabilir. Central schema servisler arası dashboard oluşturmayı kolaylaştırır.
Request Latency
Middleware full HTTP duration, interceptor handler-oriented duration ölçebilir. P95 ve P99 user impact'i average'dan daha iyi gösterir. Route template veya handler name ile histogram oluşturulabilir. Raw URL label kullanılmamalıdır. Latency breakdown database ve downstream spans ile tamamlanabilir.
OpenTelemetry Trace
OpenTelemetry distributed trace request boyunca span ilişkileri oluşturur. Automatic HTTP instrumentation ana server span'i açabilir. Interceptor controller-handler semantic span veya attribute ekleyebilir. Aynı request için gereksiz duplicate span üretmemek gerekir. Trace sampling production volume ve incident requirement'a göre ayarlanmalıdır.
Metrics
Request count, error count ve latency histogram temel HTTP metric'leridir. Active request gauge middleware veya interceptor finalize ile yönetilebilir. Route label template tabanlı olmalıdır. User ID, request ID veya raw URL metric label yapılmamalıdır. Metric naming organization standardıyla uyumlu tutulmalıdır.
Error Tracking
Unexpected exception error tracking platformuna stack ve trace ID ile gönderilebilir. Validation error'larının tamamını exception platformuna göndermek signal-to-noise oranını düşürebilir. Filter error classification owner olabilir. Interceptor duration ve handler metadata ekleyebilir. Sensitive request data error event'e otomatik eklenmemelidir.
OpenTelemetry ile Interceptor
Interceptor OpenTelemetry instrumentation'a controller ve handler düzeyinde semantic bilgi eklemek için kullanılabilir. Ancak HTTP server instrumentation zaten root span oluşturuyorsa yeni duplicate root span açmamak gerekir. Existing active span'e attributes eklemek veya child application span oluşturmak iki ayrı tasarım seçeneğidir. Error recording exception filter ve auto instrumentation ile duplicate yapılmamalıdır. Trace ID structured log context'e bağlandığında log ve distributed trace arasında hızlı geçiş sağlanır.
Span Başlatma
Tracer `startActiveSpan` ile application-specific span açabilir. Span name düşük cardinality handler identity taşımalıdır. Her küçük helper method için span açmak trace hacmini gereksiz büyütür. Controller use-case boundary iyi bir aday olabilir. Existing instrumentation hierarchy trace viewer'da kontrol edilmelidir.
Controller ve Handler Metadata
ExecutionContext class ve handler bilgisi semantic span name üretir. Raw URL parametreleri span name'e taşınmamalıdır. Controller method refactor dashboard naming'i değiştirebilir. Custom operation metadata daha stabil business operation adı sağlayabilir. Metadata standardı tracing ve metrics için ortak kullanılabilir.
Span Attributes
HTTP method, route template, tenant tier veya operation type attribute olabilir. PII veya secret attribute olarak eklenmemelidir. High-cardinality user ID kullanımı telemetry cost ve privacy riskini artırabilir. Semantic conventions mevcutsa custom isim yerine standard attribute tercih edilmelidir. Attribute count sınırlandırılarak exporter payload'ı kontrol altında tutulabilir.
Error Recording
Unexpected exception span status ve exception event olarak kaydedilebilir. Validation 400 otomatik olarak server error sayılmamalıdır. Error code business classification sağlayabilir. Stack trace telemetry backend'e gönderiliyorsa log tarafında duplicate full stack azaltılabilir. Sampling policy critical error span'larını koruyacak şekilde tasarlanabilir.
Trace ID ile Log Correlation
Active OpenTelemetry span'den trace ID alınıp structured log field'ına eklenebilir. AsyncLocalStorage logger context bunu otomatikleştirebilir. Developer incident sırasında log event'ten trace'e doğrudan geçebilir. Trace ID public response'a vermek organization policy'ye göre değerlendirilmelidir. Request ID ayrı support-friendly identifier olarak tutulabilir.
Logging'de Hassas Veri Güvenliği
Production logging debugging kolaylığı sağlarken ciddi veri sızıntısı yüzeyi oluşturabilir. Password, authorization header, session cookie ve kişisel veriler varsayılan olarak loglanmamalıdır. Request veya response body'yi komple serialize etmek özellikle global middleware ve interceptor'larda tehlikelidir. Redaction allowlist veya denylist policy merkezi logger katmanında uygulanmalıdır. Log retention ve access permission uygulama database güvenliği kadar planlı yönetilmelidir.
Password Loglamamak
Login request body loglandığında password plaintext olarak merkezi log sistemine gidebilir. Log platformu uygulama database'den çok daha geniş erişime sahip olabilir. Field adı farklı olsa bile secret detection policy uygulanmalıdır. Request body logging gerekiyorsa allowlist yaklaşımı daha güvenlidir. Test ortamında kullanılan gerçek kullanıcı credential'ları da loglanmamalıdır.
Authorization Header Loglamamak
Authorization header bearer token veya başka credential taşır. Token loga düşerse geçerlilik süresi boyunca account access riski oluşabilir. Access log middleware header dump yapmamalıdır. Debug sırasında bile token yalnız hash veya son birkaç karakter gibi güvenli representation ile gösterilmelidir. Incident sırasında token gerekiyorsa kontrollü security workflow kullanılmalıdır.
Cookie Loglamamak
Cookie session token, refresh token ve tracking identifier içerebilir. Bütün cookie header'ını loglamak gereksiz veri toplar. Belirli non-sensitive cookie gerçekten gerekiyorsa allowlist edilmelidir. Secure ve HttpOnly flag log sızıntısını önlemez. Central log redaction nested object ve raw header formatını da kapsamalıdır.
PII Masking
Email, telefon, kimlik numarası veya adres kişisel veri olabilir. Debug value gerekiyorsa maskeli representation kullanılabilir. Hash her zaman anonimleştirme sayılmaz ve tekrar tanımlama mümkün olabilir. Retention süresi business ve yasal gereksinimlere göre belirlenmelidir. Developer laptop'larına production log export edilmesi erişim kontrolünü zayıflatır.
Request Body Redaction
Body schema endpoint'e göre farklı sensitive alanlar taşıyabilir. Generic recursive redaction belirli field isimlerini silebilir, fakat unknown field'lar risk taşır. Allowlist yalnız debug için gerekli field'ları kaydetmek açısından daha güvenlidir. Large body logging performance ve storage cost'u artırır. Error tracking SDK'nin automatic request capture seçenekleri de ayrıca kontrol edilmelidir.
Response Body Logging Riskleri
Response body user profile, financial data veya access token taşıyabilir. Global interceptor response'u kolayca gördüğü için loglama cazip görünür. Production'da full response logging çoğu zaman gereksizdir. Status, payload size ve business result code yeterli olabilir. Debug sampling bile sensitive endpoint'lerde tamamen kapalı tutulmalıdır.
Metric Cardinality Problemi
Prometheus ve benzeri metric sistemlerinde label value sayısı arttıkça time series sayısı hızla büyür. Raw URL, user ID ve request ID gibi değerler yüksek cardinality oluşturur. `/users/123` ve `/users/456` ayrı label olursa her kullanıcı yeni series yaratabilir. Route template veya controller-handler metadata daha güvenli düşük cardinality seçeneklerdir. Metric tasarımı yanlışsa observability backend maliyeti ve query performance uygulamanın kendisinden daha büyük problem hâline gelebilir.
Raw URL Kullanmanın Riski
Raw URL path parametre ve query string içerir. Her unique ID ayrı metric label oluşturabilir. Query parametreleri arama terimi veya PII de taşıyabilir. Middleware route metadata bilmeden raw URL kullanmaya daha yatkındır. Interceptor handler metadata veya framework route template tercih etmelidir.
/users/123 vs /users/:id
İki request logical olarak aynı endpoint'tir. Metric label `/users/:id` olarak tutulursa tek time series oluşur. Raw `/users/123` kullanılırsa her user yeni series yaratır. Alert ve dashboard query daha kararlı olur. Route template extraction adapter ve framework version'a göre test edilmelidir.
Route Template Kullanımı
Route template düşük cardinality operation identity sunar. HTTP method ile birlikte endpoint'i yeterince ayırır. Framework instrumentation çoğu zaman semantic route attribute üretir. Custom middleware bunu kendisi tahmin etmeye çalışmamalıdır. Interceptor ExecutionContext handler name ile fallback sağlayabilir.
Prometheus Label Tasarımı
Label set önceden sınırlı value family'lerine sahip olmalıdır. Method, status class, route ve service name tipik örneklerdir. Error message label yapılmamalıdır. Tenant ID veya customer ID time series patlaması yaratabilir. High-cardinality detay logs ve traces için bırakılmalıdır.
Controller ve Handler Metadata Kullanımı
Controller ve handler isimleri build içindeki finite method set'ine bağlıdır. Bu nedenle raw URL'den çok daha kontrollü cardinality sağlar. Metric label `controller`, `handler` olarak ayrı tutulabilir. Refactor dashboard history continuity'yi etkileyebilir. Stable custom operation name decorator kullanmak uzun ömürlü sistemlerde alternatif olabilir.
Middleware ve Interceptor Performansı
Global middleware ve interceptor her request'te çalıştığı için küçük görünen maliyet yüksek trafikte büyür. Synchronous JSON serialization, blocking crypto veya network log write request latency'sini doğrudan etkileyebilir. Database query yapmak cross-cutting katmanı business data dependency'sine dönüştürür ve pool pressure yaratır. Benchmark yalnız handler code'u değil bütün request pipeline'ı ölçmelidir. Performance optimization başlamadan önce profiler ile gerçek hot path belirlenmelidir.
Global Logic'in Her Request'e Maliyeti
Bir millisecond global overhead saniyede binlerce request'te önemli CPU maliyetine dönüşebilir. Health endpoint gibi high-frequency route'lar da bu logic'i çalıştırabilir. Conditional metadata check genellikle ucuzdur, heavy body parsing değildir. Global interceptor sayısı kontrol altında tutulmalıdır. Performance budget request pipeline component bazında ölçülebilir.
Senkron İşlemler
CPU-heavy synchronous işlem Node event loop'u bloke eder. Büyük payload hash veya deep clone global middleware içinde yapılmamalıdır. Crypto gerekiyorsa uygun async API ve workload limit kullanılmalıdır. Blocking behavior p99 latency'yi belirgin artırabilir. Clinic, Node profiler veya APM flame graph ile event loop blockage ölçülebilir.
Ağır JSON Serialization
Response body'yi birden fazla interceptor tekrar serialize ederse CPU ve memory maliyeti oluşur. Structured logger object'i JSON'a çevirebilir, framework response serializer da aynı payload'ı tekrar işler. Full body log bu maliyeti büyütür. Payload size metric body content yerine daha güvenli sinyal olabilir. Large response endpoint'ler load testte ayrıca incelenmelidir.
Database Query Yapmamak
Global middleware içinde her request tenant veya user için database query yapmak database QPS'yi doğrudan HTTP QPS'ye eşitler. Cache veya token claim bilgisi kullanılabiliyorsa gereksiz query önlenebilir. Business data gerçekten gerekiyorsa guard veya service responsibility tekrar değerlendirilmelidir. Pool saturation bütün endpoint'leri etkileyebilir. Cross-cutting layer mümkün olduğunca I/O-free tutulmalıdır.
Logging I/O
Synchronous file logging request thread üzerinde latency yaratabilir. Remote HTTP logger her request'te network call yapmamalıdır. Buffered stdout ve agent-based collection yaygın production modelidir. Backpressure durumunda logger'ın application'ı bloke edip etmediği bilinmelidir. Log volume sampling ve level policy ile kontrol edilir.
Benchmark ve Profiling
Baseline pipeline ile yeni middleware veya interceptor eklenmiş pipeline karşılaştırılmalıdır. Throughput yanında P95 ve P99 latency ölçülür. CPU profile serialization veya regex hot path'i gösterebilir. Benchmark production'a yakın payload ve concurrency kullanmalıdır. Tek synthetic hello-world sonucuyla gerçek business endpoint kararı verilmemelidir.
Middleware İçinde Business Logic Kullanılmalı mı?
Middleware içinde business logic kullanmak çoğu NestJS projesinde kaçınılması gereken bir tasarımdır. Middleware route handler'dan önce görünmeyen bir koşul çalıştırdığında use-case akışını kodu okuyan geliştiricinin takip etmesi zorlaşır. Transaction boundary middleware ile service arasında dağılabilir. Unit test bir service'i doğrudan çağırdığında middleware business rule'u devre dışı kalabilir. Middleware request ID, normalization ve diğer cross-cutting HTTP concern'leriyle sınırlandırıldığında domain davranışı daha açık kalır.
Hidden Business Logic Problemi
Bir order route'una gelen request middleware içinde fiyat kuralına göre değiştiriliyorsa controller ve service kodu gerçek davranışı açıklamaz. Yeni developer yalnız service'i okuyarak sistemi anlayamaz. Aynı service queue consumer tarafından çağrıldığında middleware hiç çalışmaz. Business rule farklı giriş kanallarında tutarsızlaşır. Domain veya use-case service ortak davranışın daha doğru yeridir.
Test Edilebilirlik
Middleware business rule taşıdığında service unit test rule'u kapsamaz. E2E test zorunlu hâle gelir ve failure localization zorlaşır. Pure service method daha küçük test scope sunar. Middleware'in kendi unit testi yalnız HTTP preprocessing concern'ini doğrulamalıdır. Business testleri request framework'ünden bağımsız çalışabilmelidir.
Transaction Boundary
Database transaction business operation'ın bütün write adımlarını kapsamalıdır. Middleware'de başlayan query ile service transaction'ı ayrı connection veya context kullanabilir. Hidden side effect rollback davranışını bozar. Transaction orchestration use-case katmanında görünür olmalıdır. Interceptor transaction wrapper pattern bazı sistemlerde kullanılsa bile nested call ve transport davranışı çok dikkatli test edilmelidir.
Service Katmanının Rolü
Service application veya domain use-case'i temsil eder. Business invariants burada veya domain object'lerinde korunabilir. Controller, queue consumer ve scheduled job aynı service'i çağırdığında rule tekrar kullanılabilir. Framework-specific request object service'e sızmamalıdır. Bu ayrım Nest dışına taşınabilir test ve business logic oluşturur.
Middleware'i Cross-Cutting Concern ile Sınırlandırmak
Logging, request ID ve low-level normalization ortak concern'lerdir. Bunlar business feature değişse bile benzer kalır. Middleware'in her yeni requirement'ta biraz daha büyümesi engellenmelidir. Code review “bu logic HTTP pipeline'a mı, business use-case'e mi ait?” sorusunu sormalıdır. Basit sınır uzun vadede architecture drift'i azaltır.
Interceptor İçinde Business Logic Kullanılmalı mı?
Interceptor business logic çalıştırabilecek kadar güçlü olsa da çoğu business rule için uygun yer değildir. Handler öncesi database query veya transaction açmak controller flow'unu görünmez hâle getirebilir. Response mapping gibi presentation concern ile business decision aynı class içinde karışabilir. Interceptor cross-cutting handler davranışına odaklanmalıdır. Use-case'e özel rule service veya application layer içinde explicit çağrı olarak bulunmalıdır.
Controller Akışının Gizlenmesi
Controller yalnız `service.create()` çağırıyor görünürken interceptor arka planda başka write yaparsa actual behavior anlaşılmaz. Debug sırasında breakpoint akışı şaşırtıcı olur. Unit test controller interceptor'ı çalıştırmayabilir. Route başka transport altında reuse edilirse aynı behavior korunmayabilir. Cross-cutting olmayan logic explicit code path'e taşınmalıdır.
Database I/O
Global interceptor database query yaparsa bütün request'ler database'e ek yük bindirir. Route interceptor bile hidden extra query nedeniyle performance tahminini zorlaştırır. Audit write gerekiyorsa asynchronous event veya dedicated audit service değerlendirilebilir. Query business decision için gerekiyorsa service sorumluluğudur. Observability interceptor database connection'a bağımlı olmamalıdır.
Business Rule
Order limiti, account status veya pricing kuralı domain logic'tir. Interceptor metadata üzerinden bunları yönetmeye başlarsa annotation-driven hidden application oluşur. Service testleri gerçek rule'u doğrudan doğrulamalıdır. Guard yalnız authorization policy gibi admission concern'i yönetebilir. Business rule failure domain exception üretebilir.
Transaction Açmak
Interceptor transaction wrapper bazı architecture'larda use-case method'larını otomatik transaction içine alabilir. Ancak async context, nested transaction ve stream completion semantics dikkat gerektirir. Transaction çok uzun handler execution boyunca açık kalırsa database resource maliyeti artar. Explicit unit-of-work use-case katmanında daha görünür olabilir. Pattern seçimi benchmark ve failure testleriyle doğrulanmalıdır.
Service/Use-Case Katmanına Taşınması Gereken İşler
Database state değiştiren business operasyonları service veya use-case katmanında bulunmalıdır. Domain validation ve external service orchestration da burada daha açıktır. Interceptor yalnız timing, trace, serialization veya generic policy gibi ortak davranış ekler. Bu ayrım route handler'ın gerçek etkisini koddan takip etmeyi sağlar. Framework upgrade business rule implementation'ını daha az etkiler.
NestJS Middleware ve Interceptor Testleri
Middleware ve interceptor küçük cross-cutting bileşenler olsa da uygulamanın bütün request'lerini etkileyebilir. Unit test branch logic ve dependency behavior'ını hızlı doğrular. Integration veya E2E test gerçek lifecycle ordering ve registration scope'u kontrol eder. Interceptor Observable stream success ve error path'leri ayrı test edilmelidir. NestJS 11 migration gibi framework güncellemelerinde lifecycle regression testleri büyük güvence sağlar.
Unit Test
Middleware unit test mock request, response ve next function ile çalıştırılabilir. Interceptor test ExecutionContext ve CallHandler mock kullanabilir. External logger veya config dependency provider mock ile izole edilir. Pure operator behavior `firstValueFrom()` ile assert edilebilir. Unit test registration scope'u doğrulamadığı için E2E testin yerini tutmaz.
Mock Request/Response
Middleware yalnız kullandığı request field'larını içeren minimal mock almalıdır. Bütün Express object'i taklit etmek brittle test oluşturur. Response header method için spy yeterli olabilir. Next callback çağrı sayısı mutlaka assert edilmelidir. Early response branch'te next'in çağrılmadığı ayrıca doğrulanmalıdır.
CallHandler Mock
CallHandler `handle: () => of(value)` şeklinde kolayca mock edilebilir. Error path için `throwError(() => error)` kullanılır. Handler call count interceptor'ın `next.handle()` gerçekten çağırdığını gösterir. Cache hit branch'inde count sıfır beklenebilir. Observable async delay timeout testlerinde fake timer veya test scheduler değerlendirilebilir.
Observable Testing
Observable output `firstValueFrom()` veya subscribe assertion ile test edilebilir. Map interceptor expected envelope üretmelidir. CatchError expected exception type döndürmelidir. Finalize callback success ve error path'inde bir kez çalışmalıdır. Manual nested subscription testleri gereksiz ve zor okunur olabilir.
Dependency Injection Testleri
TestingModule interceptor veya middleware dependency graph'ını gerçek Nest container ile doğrulayabilir. Provider override ederek logger veya config fake kullanılabilir. APP_INTERCEPTOR registration global scope integration testte kontrol edilir. Circular dependency startup sırasında yakalanabilir. Request-scoped provider kullanılıyorsa lifecycle ve performance ayrıca test edilmelidir.
Request Lifecycle E2E Testi Nasıl Yazılır?
Lifecycle order'a güvenen production pipeline marker tabanlı basit E2E test ile korunabilir. Her middleware, guard, interceptor, pipe ve handler ortak bir in-memory array'e kendi marker'ını ekler. HTTP request sonrası beklenen sıra assert edilir. Error route ile exception path ayrıca doğrulanır. Framework major upgrade öncesi bu test beklenmeyen execution ordering değişikliğini hızlı şekilde ortaya çıkarır.
Middleware Marker
Middleware request başladığında `middleware` marker ekler. Bu marker guard marker'dan önce beklenir. Response finish event ayrı marker gerekiyorsa kullanılabilir. Shared array parallel testte race condition yaratmamalıdır. Test module yalnız lifecycle order için isolated uygulama oluşturabilir.
Guard Marker
Guard `canActivate` içinde `guard` marker ekler. Middleware'den sonra ve interceptor before marker'dan önce görünmelidir. Rejected guard ayrı endpoint ile test edilir. Reject durumunda pipe ve handler marker oluşmamalıdır. Middleware final response logu yine çalışabilir.
Interceptor Before Marker
Interceptor `next.handle()` çağrısından önce `interceptor-before` marker ekler. Guard marker sonrası gelmelidir. Birden fazla interceptor order ayrı marker isimleriyle doğrulanabilir. Global ve controller scopes aynı testte görülebilir. Request direction ordering böylece belgelenmiş olur.
Pipe Marker
Custom test pipe transform method'unda `pipe` marker ekler. Interceptor before sonrası ve handler öncesi çalışması beklenir. Validation error route handler marker'ı üretmemelidir. Argument başına birden fazla pipe varsa order ayrıca test edilebilir. Production code bu marker mechanism'i taşımamalıdır.
Handler Marker
Controller method çalıştığında `handler` marker ekler. Önceki bütün admission ve input aşamalarından sonra gelmelidir. Handler response basit object olabilir. Error testinde handler bilinçli exception fırlatabilir. Service marker eklenirse controller-service sırası da görülebilir.
Interceptor After Marker
Response stream `tap` veya `finalize` içinde `interceptor-after` marker üretir. Handler marker'dan sonra görünür. Birden fazla interceptor ters order ile kapanmalıdır. Error path'te `finalize` marker yine üretilebilir. Bu test first-in-last-out davranışını açıkça korur.
Exception Path Testi
Handler veya pipe kontrollü exception üretebilir. Filter expected marker ve response contract oluşturmalıdır. Success response interceptor `map` error path'te çalışmamalıdır. Finalize timing interceptor çalışmaya devam edebilir. Böylece yalnız happy path değil gerçek incident behavior'ı da test edilmiş olur.
Global Interceptor'larda Yapılan Yaygın Hatalar
Global interceptor çok geniş scope nedeniyle küçük hatayı bütün API'ye yayabilir. Her response'u aynı biçimde dönüştürmek file, stream veya third-party controller endpoint'lerini bozabilir. Full body logging security ve performance riski taşır. Ağır synchronous işlem p99 latency'yi bütün route'larda artırır. DI gerektiren interceptor'ı `new` ile manuel oluşturmak configuration ve testing sorunlarına yol açar.
Her Response'u Körlemesine Dönüştürmek
JSON object, string, null ve StreamableFile aynı response türü değildir. Generic mapper tür ayrımı yapmadan envelope uygularsa client contract bozulabilir. Metadata ile skip veya supported-type guard kullanılmalıdır. Health ve redirect endpoint'leri de değerlendirilmelidir. E2E test response type matrix içermelidir.
Binary/File Response'ları Bozmak
File download handler buffer veya StreamableFile döndürebilir. Response interceptor bunu `{ data: ... }` içine alırsa binary output geçersiz hâle gelir. Content-Type ve Content-Disposition semantics korunmalıdır. File endpoint skip metadata ile global transformation dışına alınabilir. Download E2E testi gerçek byte karşılaştırması yapabilir.
Streaming Response'ları Görmezden Gelmek
Server-sent events veya başka stream'ler uzun süre açık kalabilir. Map veya timeout interceptor normal stream davranışını değiştirebilir. Response completion metric farklı şekilde hesaplanmalıdır. Global timeout stream route'ta skip edilebilir. Streaming architecture ayrı performance ve backpressure kuralları gerektirir.
Hassas Body Loglamak
Global interceptor her response'a eriştiği için body logging kolaydır. Bu aynı zamanda bütün sensitive data'nın log platformuna taşınabileceği anlamına gelir. Production default yalnız metadata loglamalıdır. Debug body logging allowlist endpoint ve redaction ile sınırlandırılabilir. Privacy review global interceptor değişikliklerini incelemelidir.
Ağır Senkron İşlem Yapmak
Deep clone, compression veya crypto synchronous yapılırsa event loop bloke olur. Global scope bütün request'leri etkiler. Expensive transformation worker veya async architecture'a taşınabilir. CPU profile actual cost'u ölçer. “Sadece birkaç milisaniye” yüksek QPS altında ciddi kapasite kaybına dönüşebilir.
DI Gereken Interceptor'ı Manuel new Etmek
`useGlobalInterceptors(new MyInterceptor())` constructor dependency eklenene kadar çalışabilir. Daha sonra config veya logger için manual wiring başlar. `APP_INTERCEPTOR` provider DI'yi framework içinde tutar. Test override ve lifecycle behavior iyileşir. Production standardı dependency ihtimalini baştan düşünmelidir.
NestJS Middleware ve Interceptor Anti-Pattern'leri
Anti-pattern'lerin çoğu framework'ün bir iş için sunduğu doğru lifecycle bileşeni yerine daha genel bir aracı kullanmaktan çıkar. Authentication interceptor'a, validation interceptor'a veya business logic middleware'e taşındığında sorumluluklar birbirine girer. Tek dev global interceptor zamanla response, logging, cache ve error logic'i aynı class içinde toplar. `next()` veya `next.handle()` unutulması request'i sessizce durdurur. Scope order ve `@Res()` davranışı test edilmezse framework upgrade sonrasında şaşırtıcı production sorunları görülebilir.
Authentication'ı Interceptor'a Koymak
Interceptor handler wrapper'dır, route admission security aracı değildir. Authentication Nest guard ve strategy modelleriyle daha doğal yönetilir. Interceptor içinde token verify etmek public route skip branch'lerini artırır. Security behavior controller decorator'larında görünmez olabilir. Guard authorization pipeline'ı daha açık kılar.
DTO Validation'ı Interceptor'a Koymak
DTO validation pipe'ın doğal görevidir. Interceptor body'yi manuel schema ile validate etmeye başlarsa parametre bazlı decorator modelinden uzaklaşılır. Query ve path validation farklı code path'e dağılabilir. ValidationPipe ortak configuration sunar. Business validation yine service katmanında tutulabilir.
Business Logic'i Middleware'e Koymak
Middleware HTTP concern için tasarlanmıştır. Business rule request path dışındaki queue veya scheduled execution'da kaybolur. Service unit test gerçek behavior'ı kapsamaz. Transaction boundary belirsizleşir. Business logic reusable use-case katmanına taşınmalıdır.
Bütün Endpoint'lerde Tek Dev Interceptor Kullanmak
Logging, mapping, caching ve security davranışını tek class'a koymak branching'i artırır. Bir feature değişikliği bütün API'yi etkileyebilir. Küçük composable interceptor'lar daha kolay test edilir. Global interceptor sayısı yine sınırlı tutulmalıdır. Concern ownership class isminden anlaşılmalıdır.
next() Çağrısını Unutmak
Middleware response göndermiyorsa `next()` zorunludur. Unutulması request'in askıda kalmasına neden olur. TypeScript bunu otomatik olarak yakalamayabilir. Unit test next spy ile call assertion yapmalıdır. Early-return branch'leri code coverage ile kontrol edilmelidir.
next.handle() Çağrısını Unutmak
Interceptor downstream handler'ı çalıştırmak için `next.handle()` kullanır. Cache gibi bilinçli override dışında call yapılmalıdır. Return değeri unutulursa controller çalışmayabilir veya response üretilemeyebilir. Unit test handler call count'u kontrol etmelidir. Code review interceptor return path'lerini tek tek incelemelidir.
Scope Sırasına Güvenip Test Yazmamak
Framework documentation ordering modelini açıklar fakat project registration kombinasyonları ek complexity oluşturabilir. Global module veya multiple decorators davranışı upgrade sırasında değişebilir. Marker E2E test gerçek order'ı korur. Business correctness yalnız undocumented ordering assumption'a bağlanmamalıdır. Sıra önemliyse architecture comment ve test birlikte bulunmalıdır.
@Res() Kullanırken Response Interceptor Beklemek
Native response strategy Nest standard return pipeline'ını bypass edebilir. Global response mapper expected sonucu üretmeyebilir. Developer bunu interceptor bug'ı sanabilir. Passthrough seçeneği bazı low-level ihtiyaçlarda framework pipeline'ını korur. Controller response strategy code review standardında açıkça belirtilmelidir.
Production İçin Önerilen NestJS Request Pipeline
Production request pipeline sorumlulukları açık katmanlara ayırmalıdır. Middleware correlation ve raw HTTP normalization yapar, guard authentication ve authorization kararını verir. Interceptor handler timing ve trace setup sağlar, pipe input validation ve transformation uygular. Controller-service business logic'i yürütür, response tarafındaki interceptor serialization ve metrics'i tamamlar. Exception filter bütün hata yollarını ortak client contract'a dönüştürerek lifecycle'ı kapatır.
Middleware
Production middleware mümkün olduğunca erken ve hafif çalışmalıdır. Correlation ID, locale veya trusted proxy normalization burada uygulanabilir. Business data query'si yapılmamalıdır. Request context AsyncLocalStorage ile başlatılabilir. Her middleware latency ve error handling davranışı E2E testte doğrulanmalıdır.
Correlation ID
Incoming trusted correlation ID validate edilir veya yenisi üretilir. AsyncLocalStorage ve response header'a yazılır. Downstream calls aynı değeri propagate eder. Loglar bu ID üzerinden bağlanır. Metric label olarak kullanılmaz.
Raw HTTP Normalization
Header formatları ve proxy-derived bilgiler tek standarda çevrilebilir. Untrusted client header ile trusted infrastructure header ayrılır. Body business validation burada yapılmaz. Request mutation minimum tutulur. Adapter-specific behavior test edilir.
Guard
Guard request'in handler'a girip giremeyeceğini belirler. ExecutionContext sayesinde route metadata'yı bilir. Authentication ve authorization policy burada birleşebilir. Failure controller execution'ı durdurur. Security test public ve protected route'ları kapsar.
Authentication
Token veya session doğrulanır. Identity güvenilir request context'e eklenir. Expired veya invalid credential standart unauthorized response üretir. Token content loglanmaz. Public route metadata doğru biçimde uygulanır.
Authorization
Role, permission ve resource policy kontrol edilir. Tenant isolation authorization decision'a dahil edilebilir. Route metadata Reflector ile okunur. Business invariant authorization ile karıştırılmaz. Denied event security audit için ölçülebilir.
Interceptor - Before
Handler başlamadan timing ve trace enrichment yapılır. Controller ve handler metadata low-cardinality operation identity sağlar. Heavy I/O bu katmana eklenmez. Trace span existing HTTP instrumentation ile duplicate edilmez. `next.handle()` normal akışta mutlaka çağrılır.
Timing
Monotonic timestamp alınır. Handler-oriented latency ölçülür. Total HTTP duration middleware'de ayrıca bulunabilir. Metric route template veya handler ile etiketlenir. P95 ve P99 dashboard'a aktarılır.
Trace
Active span handler metadata ile zenginleştirilir. Request ID log correlation'a bağlanır. Sensitive value attribute yapılmaz. Downstream service call trace context'i taşır. Sampling strategy production volume'a göre ayarlanır.
Pipe
Pipe controller input'larını doğrular ve dönüştürür. DTO schema request contract'ın teknik sınırını tanımlar. Invalid input handler'a geçmez. Whitelist veya transform davranışı global standard olabilir. Domain validation service katmanında devam eder.
Validation
Required field, format ve primitive constraint kontrol edilir. Error response field bilgisi taşıyabilir. Sensitive input error message'de tekrar edilmez. Validation schema unit test edilir. Client ve server schema mümkün olduğunca senkron tutulur.
Transformation
Path id veya query value beklenen type'a çevrilebilir. Implicit conversion yerine açık davranış production'da daha öngörülebilir olabilir. Transform failure validation error üretir. Date veya enum conversion timezone ve case kurallarını tanımlamalıdır. Controller typed input alır.
Controller / Service
Controller transport request'i application use-case'e bağlar. Service business rule, transaction ve external dependency orchestration'ı yönetir. Framework request object service içine taşınmaz. Domain exception HTTP status bilgisine bağımlı olmayabilir. Testlerin büyük bölümü bu katmanda framework'ten bağımsız çalışabilir.
Business Logic
Business state transition açık service method'larında bulunur. Database transaction use-case boundary'ye göre yönetilir. External service failure business policy'ye göre ele alınır. Middleware veya interceptor gizli write yapmaz. Domain invariant testleri doğrudan service seviyesinde yazılır.
Interceptor - After
Handler başarılı sonuç döndürdüğünde response interceptor'ları ters sırayla çalışır. Serialization ve response mapping presentation contract'ı tamamlar. Metrics finalize success veya error sonrasında kaydedilebilir. File ve stream special-case korunur. Response body logging yapılmaz veya sıkı redaction uygulanır.
Serialization
DTO veya class serializer internal alanları public response'dan ayırır. Password ve secret alanları dışarı çıkmaz. Large object serialization benchmark edilir. Plain object ve class instance davranışı test edilir. API schema gerçek serialized output ile uyuşmalıdır.
Response Mapping
Standard `{ data, meta }` contract gerekiyorsa burada uygulanır. Double wrapping önlenir. Empty response policy belirlenir. File endpoint skip edilir. API version değişikliği backward compatibility ile yönetilir.
Metrics
Duration histogram ve status counter tamamlanır. Handler metadata low-cardinality label olarak kullanılır. Error ve success sayıları ayrı ölçülür. Request ID metric label yapılmaz. Telemetry exporter application request'ini bloklamamalıdır.
Filter
Filter exception'ı final transport error contract'a map eder. Expected 4xx ve unexpected 5xx farklı log severity alabilir. Correlation ID error body'ye eklenebilir. Stack trace yalnız internal telemetry'de kalır. Filter behavior E2E error matrix ile test edilir.
Error Contract
Error response stable machine-readable code taşımalıdır. Human-readable message client kullanıcı deneyimine uygundur. Validation details structured olabilir. Internal database veya stack trace bilgisi dışarı verilmez. Frontend ve API client error types bu contract üzerinden üretilir.
NestJS İçin Hangi Programlama Dili Kullanılır?
NestJS TypeScript ile güçlü biçimde bütünleşmiş olsa da JavaScript de destekler. Framework decorator, dependency injection ve metadata tabanlı architecture kullandığı için TypeScript büyük projelerde ciddi geliştirici deneyimi sağlar. JavaScript ile de aynı runtime framework çalışır, fakat compile-time type kontrolü azalır. Dil seçimi yalnız syntax tercihi değil ekip standardı ve codebase büyüklüğüyle ilgilidir. NestJS öğrenirken en yüksek kazanç dil tartışmasından çok request lifecycle ve framework mimarisini anlamaktan gelir.
TypeScript
TypeScript NestJS documentation ve CLI projelerinde ana geliştirme deneyimidir. DTO, provider interface ve generic interceptor type'ları daha açık tanımlanır. Compile-time hata kontrolü refactor sırasında fayda sağlar. Runtime validation yine ayrıca gerekir. TypeScript type'ı user input'un gerçekten doğru olduğunu garanti etmez.
JavaScript
NestJS plain JavaScript ile de kullanılabilir. Decorator veya modern syntax için build configuration gerekebilir. Küçük ekip mevcut JavaScript codebase'i adım adım Nest yapısına taşıyabilir. Type information JSDoc veya testlerle desteklenebilir. Büyük dependency graph ve DTO modelinde TypeScript çoğu ekip için daha fazla güvence sağlar.
TypeScript'in NestJS'teki Avantajları
Dependency constructor type'ları IDE navigation'ı kolaylaştırır. Generic `NestInterceptor` response contract'ı açıklar. DTO class'ları validation ve OpenAPI tooling ile birlikte kullanılabilir. Refactor route service signature değişikliklerini compile sırasında gösterebilir. TypeScript architecture problemlerini çözmez, fakat doğru architecture'yı daha görünür kılar.
Decorator ve Type Safety
Decorator runtime metadata sağlar, TypeScript compile-time type bilgisi sunar. İkisi aynı şey değildir. `@Body() dto: CreateUserDto` yazmak incoming JSON'u otomatik güvenli yapmaz, ValidationPipe gerekir. Custom metadata decorator typed helper ile daha güvenli tasarlanabilir. Reflector kullanımında metadata type contract merkezi tutulmalıdır.
En İyi Programlama Dili Yerine Framework Mimarisini Öğrenmek
Middleware ve interceptor görevlerini bilmeden TypeScript kullanmak iyi pipeline tasarımını garanti etmez. Node event loop, HTTP lifecycle ve RxJS behavior temel kavramlardır. Framework abstraction'ın underlying Express veya Fastify ile ilişkisi anlaşılmalıdır. Testing ve observability production yetkinliğini tamamlar. Dil bu bilgileri ifade eden araçtır, mimari kararların kendisi değildir.
Open Source ve İşbirliğinin NestJS Ekosistemindeki Rolü
NestJS açık kaynak geliştirme modeli framework behavior'ını doğrudan inceleme fırsatı sunar. GitHub issue ve pull request'leri middleware ordering veya interceptor edge case'lerinin nasıl tartışıldığını gösterir. Community package'ler logging, tracing ve starter template geliştirme süresini azaltabilir. Buna rağmen third-party package bakım durumu ve framework major version compatibility kontrol edilmelidir. Açık kaynak katkısı yalnız kod yazmayı değil reproducible bug report ve teknik iletişim becerisini de geliştirir.
NestJS'in Açık Kaynak Yapısı
Framework source code GitHub üzerinden incelenebilir. Release ve migration notları major değişiklikleri takip etmeyi kolaylaştırır. Issue history belirli behavior'ın tasarım gerekçesini anlamaya yardımcı olur. Internal API'ye production code bağlanmadan önce public documented API tercih edilmelidir. Source okumak documentation'ın yerine değil debugging desteği olarak kullanılmalıdır.
GitHub Üzerinden Katkı
Documentation typo, test veya minimal bug fix katkıya iyi başlangıçtır. Issue açarken minimal reproduction repository çok değerlidir. Framework version, Node version ve adapter açıkça yazılmalıdır. Secret veya company code paylaşılmamalıdır. Maintainer feedback teknik iletişim kalitesini geliştirir.
Community Packages
Community package seçerken son release tarihi ve maintainer activity incelenmelidir. NestJS 11 ve Node 20 compatibility doğrulanmalıdır. Package global interceptor veya middleware register ediyorsa hidden behavior dikkatle okunmalıdır. Security dependency scanning uygulanmalıdır. Basit concern için küçük custom implementation ağır dependency'den daha iyi olabilir.
Custom Middleware Paketleri
Correlation veya request context middleware reusable package hâline getirilebilir. Express-specific type dependency package scope'unu sınırlar. Configuration interface açık ve küçük tutulmalıdır. Sensitive header default logging kapalı olmalıdır. E2E test Express ve Fastify desteği iddia ediliyorsa iki adapter'da çalışmalıdır.
Custom Interceptor Paketleri
Response mapping veya timing interceptor reusable library olabilir. APP_INTERCEPTOR registration optional module API üzerinden sunulabilir. Skip metadata public decorator ile expose edilebilir. Framework internal class'larına bağımlılık minimum tutulmalıdır. Semantic versioning response contract değişikliklerinde özellikle önemlidir.
OpenTelemetry Entegrasyonları
Community tracing package automatic instrumentation ve Nest context bridge sağlayabilir. OpenTelemetry API version compatibility kontrol edilmelidir. Duplicate HTTP spans ve high-cardinality attribute riskleri test edilmelidir. Exporter failure application request'ini etkilememelidir. Custom instrumentation package behavior'ı trace viewer üzerinde doğrulanmalıdır.
Açık Kaynak Starter Kit'ler
Starter kit yeni proje için request pipeline standardını hızla oluşturabilir. Ancak içindeki auth, logging ve database seçimleri gereksinime uymayabilir. Template'i fork edip anlamadan production'a taşımak hidden configuration borcu oluşturur. Her global middleware ve interceptor tek tek incelenmelidir. Starter kit başlangıç noktasıdır, architecture kararının yerine geçmez.
Diyarbakır Yazılım Topluluğu İçin NestJS Proje Fikirleri
NestJS middleware ve interceptor konuları topluluk içinde gerçek production senaryolarına yakın açık kaynak projeler üretmek için iyi bir alan sunar. Request logging package, correlation ID demo veya standard response interceptor gibi küçük projeler yeni geliştiricilere lifecycle bilgisini uygulamalı öğretir. Topluluk projeleri incelenmek istenirse https://www.diyarbakiryazilim.com.tr/projects adresi kullanılabilir. Authentication ve gerçek zamanlı veri tarafında farklı entegrasyon örnekleri için https://www.diyarbakiryazilim.com.tr/posts/firebase-entegrasyonlari-gercek-zamanli-veri-ve-yetkilendirme içeriği de teknik çalışma fikri verebilir. Ortak projelerde amaç yalnız çalışan kod çıkarmak değil test, observability, documentation ve açık issue süreciyle sürdürülebilir backend pratiği geliştirmektir.
Ortak NestJS Starter Template
Starter template request ID, ValidationPipe, auth guard ve global error filter içerebilir. Timing interceptor ve OpenTelemetry configuration optional module olarak eklenebilir. Environment validation başlangıçta fail-fast davranmalıdır. Template dependency'leri düzenli güncellenmelidir. Yeni proje ekipleri ihtiyaç duymadığı modülleri kolayca çıkarabilmelidir.
Request Logging Middleware Paketi
Package incoming timestamp ve correlation ID üretimi sağlayabilir. Sensitive header redaction varsayılan açık olmalıdır. Express ve Fastify destek hedefi net yazılmalıdır. Output structured logger interface üzerinden customizable olabilir. Benchmark package overhead'ini gerçek değerlerle göstermelidir.
Correlation ID + OpenTelemetry Demo
Demo middleware request ID oluşturup AsyncLocalStorage context'i başlatabilir. OpenTelemetry trace ID structured loga eklenebilir. Downstream HTTP request propagation örneği sunulabilir. Trace ve request ID arasındaki fark documentation'da açıklanmalıdır. Docker Compose ile local collector kurulumu öğrenmeyi kolaylaştırabilir.
Standard Response Interceptor
Generic `{ data, meta }` response contract örneği geliştirilebilir. File ve stream endpoint skip decorator ile korunabilir. Error response ayrı exception filter tarafından üretilir. Swagger schema integration eklenebilir. Package'ın double wrapping davranışı unit testle önlenmelidir.
NestJS Request Lifecycle Workshop'u
Workshop marker E2E testiyle lifecycle sırasını canlı gösterebilir. Middleware, guard, interceptor ve pipe sırayla eklenir. Ardından error route ile filter behavior incelenir. NestJS 11 wildcard migration örneği ayrı exercise olabilir. Katılımcılar sonunda kendi production pipeline diagram'ını hazırlayabilir.
Açık Kaynak ve İşbirliği Odaklı Backend Projesi
Gerçek bir issue board ile contributor workflow kurulabilir. Good first issue etiketleri küçük middleware veya test görevleri içerebilir. Pull request template performance ve security checklist soruları ekleyebilir. Automated E2E pipeline lifecycle testlerini çalıştırabilir. Topluluk hakkında bilgi için https://www.diyarbakiryazilim.com.tr/about adresi incelenebilir.
Yazılımcılar NestJS'te Middleware ve Interceptor Konusunda Nasıl Uzmanlaşabilir?
Middleware ve interceptor konusunda uzmanlaşmak decorator syntax ezberlemekten daha fazlasını gerektirir. Node.js request-response modeli, Express veya Fastify adapter davranışı ve TypeScript temelini anlamak gerekir. Dependency injection ile RxJS bilgisi interceptor tasarımını doğrudan etkiler. Testing ve observability production davranışını görünür kılar. Açık kaynak issue incelemek ise gerçek edge case'lerle karşılaşarak framework bilgisini derinleştirir.
Node.js Request/Response Mantığı
HTTP method, headers, body ve response lifecycle öğrenilmelidir. Node event loop blocking davranışı middleware performance'ını anlamak için önemlidir. Stream ve backpressure kavramları file response veya webhook kullanımında karşınıza çıkar. TCP seviyesine inmek her gün gerekmez, fakat timeout ve aborted request davranışını anlamaya yardımcı olur. Basit bir Node HTTP server yazmak framework abstraction'ının altında ne olduğunu gösterir.
Express/Fastify
Nest adapter kullanıyor olsa da underlying platform bilinmelidir. Express middleware callback modeli ve Fastify hook/plugin yaklaşımı karşılaştırılabilir. NestJS 11 Express 5 wildcard değişiklikleri uygulamalı test edilmelidir. Native request-response API'nin adapter coupling oluşturduğu görülmelidir. Aynı küçük API iki adapter ile çalıştırmak iyi bir öğrenme egzersizidir.
TypeScript
Interface, generic, decorator ve type narrowing konuları interceptor kodunda sık kullanılır. ExecutionContext type branch'leri transport-specific type safety sağlar. DTO ve custom metadata helper typed tasarlanabilir. Runtime validation ile compile-time type ayrımı öğrenilmelidir. Strict TypeScript configuration production code kalitesini artırır.
Dependency Injection
Provider scope ve module visibility anlaşılmalıdır. Global interceptor'ın neden `APP_INTERCEPTOR` ile daha iyi DI aldığı uygulamalı görülmelidir. Circular dependency belirtileri öğrenilmelidir. Request-scoped provider'ın performance maliyeti benchmark edilebilir. TestingModule provider override pratiği yapılmalıdır.
RxJS
Observable, pipe, map, tap, catchError ve finalize temel operator'lerdir. Manual subscribe anti-pattern'i anlaşılmalıdır. Error channel ile successful value channel ayrımı öğrenilmelidir. Marble test zorunlu değildir fakat async stream mental modelini geliştirebilir. Interceptor timing ve timeout örnekleri iyi pratik sağlar.
HTTP Lifecycle
Middleware, guard, interceptor, pipe ve filter sırası çizilmelidir. Before ve after interceptor FİLO davranışı marker test ile görülmelidir. `@Res()` standard pipeline farkı test edilmelidir. Rejected guard ve validation error path'leri ayrı incelenmelidir. Lifecycle bilgisi architecture decision verirken temel referans olur.
Testing
Unit test küçük branch'leri, E2E test registration ve order'ı doğrular. Supertest veya adapter-compatible HTTP test framework kullanılabilir. Error ve timeout senaryoları happy path kadar önemlidir. Framework major version migration öncesi E2E suite çalıştırılmalıdır. Testler architecture assumptions için executable documentation görevi görür.
Observability
Structured log, metric ve trace farkları öğrenilmelidir. Cardinality problemi özellikle metrics tarafında uygulanmalı örnekle görülmelidir. OpenTelemetry trace ve request ID correlation kurulabilir. Sensitive data redaction test edilmelidir. Benchmark instrumentation overhead'ini ölçmelidir.
Açık Kaynak Projelere Katkı
Nest veya community package issue'ları gerçek edge case örnekleri sunar. Minimal reproduction hazırlamak debugging disiplinini geliştirir. Documentation contribution framework davranışını daha dikkatli okumayı gerektirir. PR review başka geliştiricilerin architecture yaklaşımını gösterir. Küçük düzenli katkı büyük tek contribution hedefinden daha sürdürülebilir öğrenme sağlayabilir.
Örnek Bir Production-Ready NestJS Pipeline Nasıl Kurulur?
Production-ready pipeline tek seferde onlarca global bileşen eklemek yerine adım adım kurulmalıdır. Önce lifecycle diagram ve concern ownership belirlenir. Request ID, authentication, validation, timing ve error contract ayrı test edilebilir bileşenler olarak eklenir. Structured logging ve OpenTelemetry ancak hangi metric ve trace'in gerçekten gerekli olduğu belirlendikten sonra devreye alınır. Son aşamada performance benchmark ve security review bütün pipeline'ın production yüküne ve risk modeline uygun olduğunu doğrular.
Adım 1 - Request Lifecycle'ı Çizme
Incoming request'ten final response'a kadar bütün Nest katmanları diagram üzerinde gösterilir. Her mevcut middleware ve interceptor doğru noktaya yerleştirilir. Business logic cross-cutting layer içinde görünüyorsa refactor adayı olarak işaretlenir. Error path ayrı oklarla gösterilir. Diagram repository architecture documentation içinde version control altında tutulabilir.
Adım 2 - Request ID Middleware
İlk gerçek bileşen correlation veya request ID middleware olabilir. Trusted incoming ID validate edilir veya UUID oluşturulur. Response header ve AsyncLocalStorage context güncellenir. Middleware unit ve E2E test edilir. Logger sonraki adımlarda aynı context'i kullanır.
Adım 3 - Authentication Guard
Authentication strategy credential'ı doğrular ve identity üretir. Guard protected route admission kararını verir. Public route metadata custom decorator ile tanımlanabilir. Authorization gerekiyorsa ikinci policy guard eklenebilir. Invalid token testleri error contract ile doğrulanır.
Adım 4 - ValidationPipe
Global ValidationPipe request DTO contract'ını uygular. Whitelist ve transform ayarları bilinçli seçilir. Unknown property behavior security testle doğrulanır. Route parametre conversion ayrı pipe kullanabilir. Error filter validation details'i standard API contract'a dönüştürür.
Adım 5 - Timing Interceptor
Interceptor controller ve handler metadata'sını alır. Monotonic başlangıç timestamp'i kaydedilir. Finalize sırasında duration histogram güncellenir. Raw URL yerine low-cardinality operation label kullanılır. Benchmark interceptor overhead'inin kabul edilebilir olduğunu doğrular.
Adım 6 - Response Serialization
Response DTO ve ClassSerializerInterceptor strategy seçilir. Sensitive entity alanları dışarı çıkarılmaz. Generic response envelope gerçekten gerekiyorsa ayrı interceptor eklenir. File ve stream endpoint behavior test edilir. OpenAPI schema serialized output ile karşılaştırılır.
Adım 7 - Exception Filter
Global filter expected ve unexpected exception'ları ortak error schema'ya çevirir. Internal stack yalnız log veya error tracker'a gider. Correlation ID response'a eklenebilir. Domain error code HTTP status mapping tablosu oluşturulur. Error E2E matrix 400, 401, 403, 404, 409 ve 500 örneklerini kapsar.
Adım 8 - Structured Logging
Logger requestId, handler, status ve duration gibi field'ları JSON olarak üretir. Password ve token redaction default açık olur. Full body logging kapalı tutulur. Log sink application request'i synchronous network I/O ile bloklamaz. Production volume için retention ve sampling policy belirlenir.
Adım 9 - OpenTelemetry
HTTP automatic instrumentation önce devreye alınır. Interceptor yalnız eksik semantic handler context'ini ekler. Trace ID log correlation'a bağlanır. Downstream HTTP, database veya messaging instrumentation test edilir. Span cardinality ve exporter overhead benchmark edilir.
Adım 10 - E2E Lifecycle Testi
Marker test middleware, guard, interceptor, pipe ve handler sırasını doğrular. Error route filter behavior'ını test eder. `@Res()` kullanan special endpoint varsa interceptor expectation ayrıca yazılır. NestJS upgrade öncesi test suite çalıştırılır. Lifecycle order architecture document ile aynı kalmalıdır.
Adım 11 - Performance Benchmark
Baseline ile bütün pipeline etkin hâl karşılaştırılır. P50, P95, P99 latency ve throughput ölçülür. CPU profile global serialization veya logging hot spot'larını gösterir. Large payload ve high concurrency senaryoları dahil edilir. Performance bütçesini aşan concern optimize edilir veya scope daraltılır.
Adım 12 - Security Review
Authentication ve authorization bypass route'ları kontrol edilir. Logging redaction ve response serialization sensitive data açısından incelenir. Security headers ve CORS policy doğrulanır. Dependency ve Node runtime support durumu kontrol edilir. Penetration veya abuse scenario testleri pipeline'ın yalnız normal kullanıcı akışında değil kötü niyetli girdilerde de güvenli olduğunu gösterir.
Sıkça Sorulan Sorular
NestJS middleware ve interceptor konusunda soruların büyük bölümü lifecycle sırası ve sorumluluk dağılımı etrafında toplanır. Bir feature teknik olarak birden fazla katmanda uygulanabilse de framework'ün sunduğu semantic role doğru seçildiğinde kod daha kolay test edilir. Middleware HTTP preprocessing, guard admission, pipe input validation, interceptor handler wrapper ve filter error contract için güçlü varsayımlardır. Scope ve DI registration biçimi de production davranışını doğrudan etkiler. Aşağıdaki yanıtlar günlük geliştirmede en sık karşılaşılan karar noktalarını özetler.
NestJS middleware nedir?
NestJS middleware route handler'dan önce çalışan request-response pipeline bileşenidir. Request, response ve next callback'e erişebilir. Functional function veya `NestMiddleware` uygulayan injectable class olarak yazılabilir. Request ID, cookie parsing ve HTTP normalization için uygundur. Response'u bitirmiyorsa `next()` çağrısı zorunludur.
NestJS interceptor nedir?
Interceptor controller handler'ını before-after logic ile çevreleyen Nest bileşenidir. `ExecutionContext` ve `CallHandler` kullanır. `next.handle()` Observable üzerinden response transformation, logging ve error observation yapılabilir. Cache hit gibi durumda handler tamamen skip edilebilir. Business logic yerine cross-cutting handler concern'leri için kullanılmalıdır.
Middleware ile interceptor arasındaki fark nedir?
Middleware request lifecycle'da daha erken çalışır ve handler metadata'sını doğal olarak bilmez. Interceptor guard sonrasında devreye girer ve hangi controller method'un çalışacağını bilir. Middleware raw request preprocessing için uygundur. Interceptor response stream'i değiştirebilir. Logging iki katmanda da yapılabilir fakat ölçülen scope farklıdır.
NestJS request lifecycle sırası nedir?
Genel HTTP akışı incoming request, middleware, guards, interceptor before, pipes ve handler yönünde ilerler. Handler service çağrılarını tamamladıktan sonra interceptor response tarafı ters sırada çözülür. Exception oluştuğunda filters uygun error handling yapar. Sonuç underlying HTTP adapter üzerinden client'a gönderilir. Scope içi ordering için resmi lifecycle modeli ve project E2E testleri birlikte kullanılmalıdır.
Middleware mi interceptor mı önce çalışır?
Middleware interceptor'dan önce çalışır. Guards da middleware sonrasında ve interceptor öncesinde çalışır. Bu nedenle request başlangıç correlation ID'si middleware'de hazırlanabilir. Interceptor daha sonra aynı ID'yi handler metadata'yla birlikte kullanır. Unauthorized guard request'i inner interceptor'a hiç ulaşmayabilir.
Guard mı interceptor mı önce çalışır?
Guard interceptor'dan önce çalışır. Guard request'in route handler'a girmesine izin vermezse interceptor veya handler execution başlamaz. Authorization için guard'ın uygun olmasının nedenlerinden biri budur. Interceptor yalnız izin verilmiş execution'ı çevreler. Middleware access log yine guard rejection'ı görebilir.
Pipe mı interceptor mı önce çalışır?
Request yönünde interceptor'ın before bölümü pipe'lardan önce çalışır. Ardından pipes controller argument'larını validate veya transform eder. Handler tamamlandıktan sonra interceptor response bölümü çalışır. Bu nedenle timing interceptor pipe süresini de kapsayabilir. Marker E2E test exact project behavior'ını doğrulayabilir.
NestJS interceptor response'u değiştirebilir mi?
Evet, `next.handle().pipe(map(...))` ile handler'ın dönüş değeri değiştirilebilir. Standard response envelope bunun yaygın örneğidir. Native `@Res()` ile response doğrudan gönderiliyorsa mapping beklenen şekilde çalışmayabilir. File ve streaming response'lar global mapper'dan korunmalıdır. API contract değişikliği integration test ile doğrulanmalıdır.
Middleware response'u değiştirebilir mi?
Middleware native response object üzerinde header ekleyebilir veya response'u tamamen sonlandırabilir. Ancak controller return stream'ini interceptor gibi doğal biçimde map etmez. Middleware response'u erken bitirirse sonraki Nest lifecycle çalışmaz. Security header veya request ID response header'ı eklemek uygun kullanım örneğidir. Standard body transformation için interceptor daha doğru araçtır.
Logging için middleware mi interceptor mı kullanılmalıdır?
Incoming access log ve full HTTP timing için middleware güçlüdür. Controller-handler metadata ve execution duration için interceptor daha güçlüdür. Guard rejection visibility gerekiyorsa yalnız interceptor yeterli değildir. Production'da iki katman farklı event türleriyle birlikte kullanılabilir. Duplicate body ve stack logging yapılmamalıdır.
Authentication middleware'de mi guard'da mı yapılmalıdır?
Nest uygulamalarında route admission'a bağlı authentication guard veya authentication strategy ile yönetilmesi daha uygundur. Middleware generic credential extraction yapabilir. Route public veya protected metadata'sı guard tarafından görülebilir. Authorization kesinlikle guard veya policy katmanında tutulmalıdır. Security design framework lifecycle ile uyumlu olmalıdır.
Validation interceptor'da mı pipe'da mı yapılmalıdır?
Input DTO ve parameter validation pipe'a aittir. ValidationPipe global standard sağlayabilir. Interceptor handler wrapper ve response stream concern'leri için kullanılmalıdır. Business validation service veya domain katmanında ayrıca çalışabilir. Bu ayrım input contract ile business rule'u birbirinden ayırır.
Global interceptor nasıl tanımlanır?
`app.useGlobalInterceptors()` ile manuel global instance kaydedilebilir. Dependency injection gerekiyorsa `APP_INTERCEPTOR` provider daha uygundur. Global interceptor bütün controller route'larını etkiler. HTTP dışı transport varsa context kontrol edilmelidir. Performance ve special response istisnaları E2E test edilmelidir.
APP_INTERCEPTOR nedir?
`APP_INTERCEPTOR` Nest core global interceptor provider token'ıdır. Module provider olarak kullanılır. `useClass`, `useExisting` veya `useFactory` ile registration yapılabilir. DI container avantajları korunur. Provider'ın module konumu etki scope'unu daraltmaz, interceptor yine global olur.
next.handle() ne işe yarar?
`next.handle()` interceptor zincirinde downstream execution'ı başlatır. Sonunda controller handler'ın response Observable'ını döndürür. Map, tap veya catchError bu stream'e uygulanabilir. Çağrı yapılmazsa handler çalışmaz. Cache hit gibi bilinçli override dışında normal interceptor bu çağrıyı yapmalıdır.
ExecutionContext nedir?
ExecutionContext çalışan controller, handler ve transport hakkında bilgi sağlar. `getClass()` ve `getHandler()` metadata access için kullanılır. HTTP, RPC ve WebSocket context'e geçiş yapılabilir. GraphQL ayrı GqlExecutionContext dönüşümü kullanır. Reusable guard ve interceptor tasarımının ana framework abstraction'larından biridir.
CallHandler nedir?
CallHandler interceptor'a downstream handler stream'ini açan interface'tir. `handle()` method'u Observable döndürür. Interceptor bu Observable'ı transform edebilir. Mock CallHandler unit testte kolayca `of()` veya `throwError()` üretebilir. Handler execution'ı kontrol eden temel araçtır.
NestJS interceptor neden Observable döndürür?
Observable handler response ve error lifecycle'ını composable stream olarak temsil eder. RxJS operator'leri before-after logic, mapping ve timeout gibi concern'leri declarative ifade eder. Promise handler'lar da framework tarafından bu model içinde ele alınabilir. Manual subscribe etmek yerine Observable framework'e geri döndürülmelidir. FİLO interceptor davranışı stream composition ile doğal olarak oluşur.
@Res() interceptor'ı neden bozabilir?
Native response object kullanıldığında controller response'u kendisi gönderir. Nest standard return-value pipeline'ı artık body'yi yönetmeyebilir. Response mapping ve CacheInterceptor gibi özellikler bu nedenle çalışmayabilir. Passthrough seçeneği bazı native response işlemlerinde standard pipeline'ı korur. Manual response yalnız gerekli low-level senaryolarda tercih edilmelidir.
NestJS 11'de middleware wildcard nasıl yazılır?
Named wildcard kullanımı güncel güvenli yaklaşımdır. Alt path için `*splat`, root dahil optional wildcard için `{*splat}` kullanılabilir. Prefix ile `users/{*splat}` benzeri pattern kurulabilir. Eski `*` kullanımları migration sırasında gözden geçirilmelidir. Express 5 ve Fastify middleware matching E2E testle doğrulanmalıdır.
NestJS middleware Express ve Fastify'da farklı mıdır?
Evet, underlying adapter request-response ve middleware integration ayrıntıları farklıdır. Express middleware ekosistemi `(req, res, next)` modeline dayanır. Fastify kendi hook ve plugin sistemine sahiptir. Nest abstraction farkları azaltır fakat native type kullanımı coupling oluşturur. Adapter-agnostic code business ve interceptor katmanında daha kolay korunur.
WebSocket'lerde interceptor kullanılabilir mi?
Evet, Nest interceptor WebSocket gateway ve message handler'larda kullanılabilir. ExecutionContext WebSocket client ve data erişimi sunar. HTTP status veya response header varsayımı yapılmamalıdır. Message timing ve trace gibi concern'ler reusable olabilir. High-frequency message stream'lerinde logging overhead ölçülmelidir.
Microservice'lerde interceptor kullanılabilir mi?
Evet, Kafka, RabbitMQ, NATS ve diğer Nest microservice handler'larında interceptor kullanılabilir. RPC context transport-specific metadata sağlar. Timing, tracing ve error observation ortak concern olarak uygulanabilir. HTTP response transformation semantics doğrudan kopyalanmamalıdır. Retry ve acknowledgement behavior ilgili transport modeline göre ayrıca tasarlanmalıdır.
Nest.js Middleware ve Interceptor nasıl kullanılır ve aralarındaki farklar nelerdir?
Nest.js Middleware ve Interceptor Kullanımı için önce hangi concern'in request lifecycle'ın hangi noktasına ait olduğunu belirlemek gerekir. Middleware request-response pipeline'ın başında çalışır ve request ID, header normalization veya raw HTTP preprocessing için uygundur. Interceptor ise handler metadata'sına erişir ve `next.handle()` üzerinden response stream'ini çevreler. Response transformation, timing ve cache gibi before-after davranışlarda interceptor tercih edilir. Authentication ve validation gibi görevler ise sırasıyla guard ve pipe katmanında tutulmalıdır.
Nest.js request lifecycle içinde Middleware ve Interceptor hangi sırayla çalışır?
Incoming HTTP request önce middleware zincirinden geçer. Ardından guards çalışır ve request kabul edilirse interceptor'ların before bölümü başlar. Pipes argument validation veya transformation yaptıktan sonra controller ve service çalışır. Response handler'dan çıktığında interceptor'lar ters sırada after logic'lerini tamamlar. Exception oluşursa uygun filter final error response'u üretir.
Nest.js’te global, controller ve route seviyesinde Interceptor nasıl tanımlanır?
Route veya controller seviyesinde `@UseInterceptors()` decorator kullanılabilir. Global dependency-free kullanım için `app.useGlobalInterceptors()` seçeneği vardır. DI gereken global interceptor `APP_INTERCEPTOR` provider token ile module içinde kaydedilmelidir. Controller scope bütün class handler'larını, route scope yalnız tek method'u etkiler. Birden fazla scope kullanıldığında request ve response order E2E testle doğrulanmalıdır.
Logging, response transformation, caching ve performans ölçümü için Middleware mi yoksa Interceptor mı tercih edilmelidir?
Incoming access logging ve correlation ID üretimi için middleware güçlü seçimdir. Handler duration ve controller metadata tabanlı performans ölçümü interceptor ile daha iyi yapılır. Response transformation doğrudan interceptor concern'idir. Cache de handler execution'ı skip edebildiği için interceptor modeline uygundur. Büyük production sisteminde middleware ve interceptor birbirinin alternatifi değil farklı lifecycle katmanlarını tamamlayan araçlar olarak kullanılmalıdır.
Nest.js Middleware ve Interceptor kullanımı konusunda yakınımda danışmanlık veya eğitim nerede bulabilirim?
NestJS ve Node.js backend danışmanlığı yakınımda araması yaparken yalnız framework syntax bilen değil request lifecycle, observability, security ve performance konularını birlikte ele alan ekipleri değerlendirmek yararlıdır. Kurumsal NestJS backend geliştirme ve performans optimizasyon hizmeti için mevcut middleware, interceptor ve dependency akışının önce ölçülmesi gerekir. Diyarbakır'da teknik topluluk çalışmaları, ortak projeler ve workshoplar bu konuda uygulamalı öğrenme ortamı sağlayabilir. Diyarbakır Yazılım Topluluğu hakkında bilgi için https://www.diyarbakiryazilim.com.tr/about adresine ulaşabilirsiniz. Açık proje çalışmalarını görmek için https://www.diyarbakiryazilim.com.tr/projects adresi incelenebilir.
Sonuç: NestJS Request Pipeline'ını Sorumluluklara Göre Tasarlayın
Nest.js Middleware ve Interceptor Kullanımı doğru yapıldığında controller kodunu inceltir, observability standardını güçlendirir ve framework davranışını daha öngörülebilir hâle getirir. Middleware request'i erken hazırlamalı, guard erişim kararını vermeli, pipe input'u doğrulamalı, interceptor handler execution'ı çevrelemeli ve exception filter final error contract'ı yönetmelidir. NestJS 11'e geçerken Express 5 wildcard syntax, middleware registration order, Node.js 20+ runtime ve adapter-specific davranışlar mutlaka integration testlerle doğrulanmalıdır. Global interceptor ve middleware'leri küçük, ölçülebilir ve business logic'ten bağımsız tutmak uzun ömürlü backend sistemlerinin bakımını ciddi biçimde kolaylaştırır. NestJS, Node.js ve açık kaynak backend çalışmaları için Diyarbakır Yazılım Topluluğu'na https://www.diyarbakiryazilim.com.tr üzerinden ulaşabilir, proje örneklerini https://www.diyarbakiryazilim.com.tr/projects adresinden inceleyebilirsiniz.
share: