NestJS Controller Kavramı

Basit Olarak Controller Nedir?

Temel olarak bir controller gelen istekleri karşılamak ve daha sonrasında isteği yapan client’a cevap döndürme işini yapar. Bu herhangi bir X frameworkünde de aynı şekilde işlemektedir.

Controllerlar frameworklerin kullanıldığı dile göre farklı şekillerde görünebilirler ve o frameworkün tasarımına göre değişikliklere sahip olabilirler.

Controllerlar bir ya da daha fazla route’a sahip olabilirler ve bu routelar methodlardan oluşabilir. Bu methodlar istek türüne göre farklı görevler yerine getirirler.

NestJS ve Controller Yapısı

NestJS ile controller oluşturmak istiyorsanız sınıflar ve decoratorleri kullanmalısınız. Daha önce Python kullandıysanız decoratörlere aşinasınızdır bu aynı zamanda .NET teknolojisine sahip diller için attribute kavramı olarak geçerli.

Nest controllerları için gereken decorator @Controller() şeklindedir. Default olarak bulunduğu dosyanın adını alırlar. Örnek vereyim;

@Controller()
export default class UsersController {
  // ...
}

Bu şekilde çalışan controller’ın endpoint’i varsayılan olarak /users şeklinde olacaktır. Eğer class adına rağmen kendi endpointinizi istiyorsanız şöyle yapmalısınız

@Controller('kullanicilar')
export default class UsersController {
  // ...
}

Böylelikle artık yeni endpoint değerimiz /kullanicilar oldu. Yani bu decoratörümüz string olarak prefix değeri alıyor diyoruz. Tabii ki controllerlar object olarak veri de alıyor. Bu scope’u değiştirme imkanını bize sunuyor ama bu biraz advanced konu. Ben de henüz hakim değilim.

Controllerlar providerları constructorlarinda alarak dependency injection mekanizmasını çalıştırmanıza olanak tanırlar.

@Controller('kullanicilar')
export default class UsersController {
  constructor(private readonly userService: UserService) {}
}

Eğer manuel değil CLI ile controller oluşturacaksanız aşağıdaki komut size yetiyor.

nest g controller users

Routing Yapısı

Controllerlar bizim ana yapımızdı. Bunları basit olarak yukarıdaku gibi oluşturabiliriz. Fakat routing işlemine gelecek olursak farklı HTTP verbleri ile çalışmamız gerekebilir. Bunlar GET, POST, PUT, DELETE, HEAD, PATCH, OPTIONS olmaktalar. Örneğin tüm kullanıcıları getiren bir endpoint yazalım.

@Controller('kullanicilar')
export default class UsersController {

  constructor(private readonly userService: UserService) {}

  @Get()
  async findAll(): Promise<UsersModel[]> {
    return await this.userService.findAll();
  }
}

Client’tan /kullanicilar adresine GET isteği atıldığında tüm kullanıcıların listelendiği sonuç dönecektir. Örneğin kullanıcının profilini veren bir adresimiz olacak. Bu da şöyle olacak diyelim /kullanicilar/profilim

@Controller('kullanicilar')
export default class UsersController {

  constructor(private readonly userService: UserService) {}

  @Get()
  async findAll(): Promise<UsersModel[]> {
    return await this.userService.findAll();
  }

  @Get('profilim')
  async myProfile(): Promise<UsersModel> {
    return await this.userService.myProfile();
  }
}

Bu arada isteklerinizde 200’e göre kontrol sağlıyorsanız belirteyim, POST request default olarak 201 kodunu döner. Bunu override etmek için @HttpCode(200) şeklinde bir decorator’u de yine methodun başına koymalısınız.

Request Nesnesi

Bazı durumlarda client tarafına dair bilgilere erişmemiz gerekebilir. Nest default olarak bazı verileri bize sunuyor olsa da kullanılan platformun (Örneğin Express) nimetlerinden faydalanmanıza da imkan tanıyor. Örneğin şöyle bir şey var.

import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('kullanicilar')
export default class UsersController {

  constructor(private readonly userService: UserService) {}

  @Get()
  async findAll(@Req() request: Request): Promise<UsersModel[]> {
    return await this.userService.findAll();
  }
}

Bu @Req() decoratoru bazı özellikleri bize sağlıyor. Örneğin client’ın IP bilgisini almak istiyorsanız req.ip demeniz yeterli oluyor.

Resource Kavramı

Yukarıda belirttiğim gibi GET, POST, PUT, DELETE, HEAD, PATCH, OPTIONS verblerinin her birini içeren endpointler oluşturabilirsiniz. Bunlar resource olarak geçer. Ayrıca bir de @All() mevcuttur.

import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('kullanicilar')
export default class UsersController {

  constructor(private readonly userService: UserService) {}

  @Post()
  async create(@Req() request: Request): Promise<UsersModel> {
    return await this.userService.create(req.body);
  }

  @Get()
  async findAll(@Req() request: Request): Promise<UsersModel[]> {
    return await this.userService.findAll();
  }
}

Header Ayarları

İstersek @Header() decoraturunu kullanarak header ayarlamalarını yapabiliriz. O route’a özel belirli headerlar set edebiliriz. Örneğin şöyle

import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('kullanicilar')
export default class UsersController {

  constructor(private readonly userService: UserService) {}

  @Post()
  @Header('Cache-Control', 'none')
  async create(@Req() request: Request): Promise<UsersModel> {
    return await this.userService.create(req.body);
  }

  @Get()
  async findAll(@Req() request: Request): Promise<UsersModel[]> {
    return await this.userService.findAll();
  }
}

Yönlendirme İşlemleri

Diyelim ki bir resource artık kullanılmıyor ya da yönlendirilmesi gerekiyor. Bu durumda yönlendirme nasıl olacak? Onu da @Redirect() decoratörü ile halledebilirsiniz.

import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('kullanicilar')
export default class UsersController {

  constructor(private readonly userService: UserService) {}

  @Get()
  @Redirect('https://aligoren.com', 301)
  async findAll(@Req() request: Request): Promise<UsersModel[]> {
    return await this.userService.findAll();
  }
}

Route Parametreleri

RESTFul işlemlerde belirli şekillerde adreslere erişimlerimiz oluyor. Örneğin kullanicilar/ali şeklinde bir adres. Bu durumlarda dinamik değerler tutan parametrelerimiz var demektir.

import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('kullanicilar')
export default class UsersController {

  constructor(private readonly userService: UserService) {}

  @Get(':username')
  async findByUsername(@Param() params): Promise<UsersModel> {
    return await this.userService.findByUsername(params.username);
  }
}

ya da spesifik olarak aşağıdaki belirtebilirsiniz

import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('kullanicilar')
export default class UsersController {

  constructor(private readonly userService: UserService) {}

  @Get(':username')
  async findByUsername(@Param('username') username): Promise<UsersModel> {
    return await this.userService.findByUsername(username);
  }
}

Bahsetmek istediklerim kısaca bu kadar. Daha detaylı konuları da umarım enerjim olursa paylaşabilirim. Hatalı / Eksik gördüğünüz noktaları lütfen belirtin.

Şimdilik okuduğunuz için teşekkür ederim.

Kaynaklar