NestJS Kullanırken Client IP Adresini Alma

Selamlar. Bir süredir NestJS ile ilgileniyorum. Bu yazıda karşılaştığım bir problemin nasıl basitçe çözüleceğini anlatacağım.

Ne Yapıyordum?

Bir adet middleware yazmıştım. Bu middleware API üzerine gelen istekleri loglamaya yarıyordu. Temel olarak aradığım şeyler request için url, method ve client ip bilgisi ile response için de statusCode ile statusMessage şeklindeydi.

Sorunlar Nelerdi?

Birden fazla sorunum vardı bunları tabii ki biraz geç öğrendim. Öncelikle client IP’yi almam gerekiyordu.

Oluşturduğum middleware şuna yakındı;

@Injectable()
export default class LoggerMiddleware implements NestMiddleware {

    use(req: Request, res: Response, next: Function) {

        next();

        const response = res.json()

        const { url, method, ip } = req;
        const { statusCode, statusMessage, } = response;

        const ipAddress = ip.split(':').reverse()[0];

        const statusSummary = {
            request: { url, method, ipAddress }, 
            response: { statusCode, statusMessage }
        }

        console.log('Status Logged', statusSummary);
    }
}

Burada ilk problem, request Interface’i herhangi bir ip tanımına sahip değildi. Bunun için iki çözüm yolu vardı.

İlk çözüm için npm üzerindeki request-ip paketini kurmaktı. Önce öyle denedim.

npm install --save-dev request-ip

Bu sayede ip adresini alabiliyordum. Aşağıdaki kod main.test dosyasının içeriğini göstermektedir.

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.use(requestIp.mw());

  await app.listen(3000);
}
bootstrap();

Bu sayede Express factory için clientIp adında bir key ile IP adresini alabiliriz. Bu ilk yöntemdi. Sonradan fark ettim ki aslında express’in Request ve Response interface’lerini de kullanabilirmişim.

Bu interface zaten ip adında bir key’e sahipmiş.

import { Request, Response } from "express";

Eğer böyle import etmezseniz, nest’in kendi interfacelerini alırsınız. Ederseniz de express’e ait olanı kullanırsınız.

Artık bunu elastic üzerine mi loglarsınız naparsınız size kalmış 🙂

Bir diğer yöntem ise x-powered ile gelen uygulama framework bilgisini içeren header bilgisini disable etmek istiyordum.

Normalde main.ts içindeki kodumuz şöyle geliyor;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  await app.listen(3000);
}
bootstrap();

Bunun yerine aşağıdaki interface’i import edip bootstrap fonksiyonumu şöyle düzenliyorum;

import { NestExpressApplication } from '@nestjs/platform-express'

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);

  app.disable('x-powered-by');

  await app.listen(3000);
}

Artık header bilgisinde x-powered-by bilgisi bulunmayacak. Tabii ki siz derseniz ki ben kendim bunu değiştirmek istiyorum. O zaman o ilgili middleware içerisine şunu yazabilirsiniz;

res.setHeader('x-powered-by', 'Buralar ohooo neler içeriyor');

Bunu next fonksiyonundan önce basmanız gerekiyor. Middleware için örnek son kod;

@Injectable()
export default class LoggerMiddleware implements NestMiddleware {

    use(req: Request, res: Response, next: Function) {

        res.setHeader('x-powered-by', 'Buralar ohooo neler içeriyor');

        next();

        const response = res.json()

        const { url, method, ip } = req;
        const { statusCode, statusMessage, } = response;

        const ipAddress = ip.split(':').reverse()[0];

        const statusSummary = {
            request: { url, method, ipAddress }, 
            response: { statusCode, statusMessage }
        }

        console.log('Status Logged', statusSummary);
    }
}

Bu Middleware Nasıl Kullanılıyor?

Bunun için app.module.ts dosyasında yer alan AppModule class’ını şöyle düzenliyoruz;

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import LoggerMiddleware from './middlewares/logger.middleware';

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*')
  }
}

forRoute('*') derseniz bütün routelar için bu middleware çalışsın dersiniz. Benim amacım da buydu.

Hepsi bu kadar 🙂 Hatalı bilgiler varsa lütfen belirtin, birlikte düzeltelim. Teşekkürler 🙂

Kaynaklar