Selamlar. Bu yazıda NestJS’in temel kavramlarından olan provider kavramını anlatmaya çalışacağım.
NestJS’de Provider Nedir?
NestJS özelinde konuşacağım için her birini teker teker belirtmeden söylemem gerekiyor. Providerlar servisler, repositoryler, factoryler ya da helperlar olabilir ya da buna benzer herhangi bir şey.
Providerların ana kullanım amacı bağımlılıkları inject edebilmesi. Bu sayede oluşturduğunuz nesneler birbirleri ile ilişkisel halde bulunabiliyorlar ve böylelikle Nest’in runtime’ına basit bir şekilde aktarılabiliyorlar.
Şimdi buraya kadar biraz karmaşık gelmiş olabilir. O nedenle örneklerle gideceğiz.
Biraz Daha Detaylandıralım
Bir önceki yazıda controller kavramını anlatmıştım. Burada controllerların HTTP isteklerini karşıladığını ve bunları işlediğini açıklamıştım.
Bu controllerlar aldıkları verileri oluşturulan servisler, factory sınıflar ya da helperlar sayesinde işleyebilirler. Bir provider basit olarak JavaScript class’ıdır. Sadece class tanımında @Injectable()
decoratörünü kullanırlar. Bunu örnekle görmenin daha iyi olacağını düşünüyorum.
İlk Providerımız
Örneğin OgrencilerService
adında bir servis oluştruacağımızı varsayalım. Bu servis veriye erişimden ve veriyi tutmadan sorumlu olacak ve yine OgrencilerController
tarafından erişilebilir şeklinde tasarlanmış olacaktır. Bu basit haliyle böyle olacak. Hadi başlayalım;
ogrenciler.service.ts
import { Injectable } from '@nestjs/common';
import IOgrenciler from './interfaces/ogrenciler.interfaces';
@Injectable()
export default class OgrencilerService {
private readonly ogrenciler: IOgrenciler[] = [];
async create(ogrenci: IOgrenciler) {
this.ogrenciler.push(ogrenci);
}
async findAll(): Promise<IOgrenciler[]> {
return this.ogrenciler;
}
}
Evet hepsi bu. Kısacası bu servis öğrenci oluşturmada ve öğrencileri listelemede bize yardımcı olacak bir servis. Bu arada bunu NestCLI kullanarak yapmak isterseniz
nest g service ogrenciler
şeklinde de oluşturabilirsiniz.
Servisimiz anladığımız şekliyle IOgrenciler
interface’ini kullanıyor. Onun şöyle olduğunu düşünelim;
ogrenciler.interface.ts
export default interface IOgrenciler {
adSoyad: string,
yas: number,
ogrenciNo: number
}
Oluşturulan Servisin Inject Edilmesi
Şimdi gelelim bu servisi nasıl kullanacağımıza. Örneğin OgrencilerController
adında bir controller oluşturduk.
ogrenciler.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import OgrencilerDto from './dto/ogrenciler.dto';
import OgrencilerService from './ogrenciler.service';
import IOgrenciler from './interfaces/ogrenciler.interface';
@Controller('ogrenciler')
export default class OgrencilerController {
constructor(private readonly ogrencilerService: OgrencilerService) {}
@Post()
async create(@Body() ogrencilerDto: OgrencilerDto) {
this.ogrencilerService.create(ogrencilerDto);
}
@Get()
async findAll(): Promise<IOgrenciler[]> {
return this.ogrencilerService.findAll();
}
}
Yukarıdaki örnek kod parçasında controller’ın constructor methodunda OgrencilserService
isimli servisin inject edildiğini görüyoruz. Bu işlemde private
syntax’inin kullanıldığına dikkat edelim. Bu sayede hem tanımlama hem de aynı anda initialize etmemize yardımcı olur.
Dependency Injection
Nest dahili olarak Dependency Injection destekleyerek gelir. Bununla ilgili bir angular dokümanı da öneriyorlar. 1
TypeScript’in kapasitesine bağlı olarak NestJS’de bağımlılıkların yönetilmesi bir hayli kolaylaştırılmış. Çünkü sadece türe bağımlı olarak çözülüyorlar. Örneğin aşağıdaki örnekte ogrencilerService
değişkeni, OgrencilerService
için bir instance oluşturur ve döner.
constructor(private readonly ogrencilerService: OgrencilerService) {}
Scope Kavramı
Bu konuyu detaylı olarak sonradan göreceğiz
Providerlar varsayılan şekilde scope ile senkronize edilmiş bir lifecycle’a sahiptir. Uygulama ilk başlatıldığında tüm bağımlılıklar çözümlenmiş olmalı. Aynı şekilde uygulama durdurulduğunda da sonlandırılmalılar. Default olarak @Injectable()
için scope değeri SINGLETON
şeklinde ayarlanmıştır.
Özel Providerlar
Nest dahili olarak providerlar arası ilişkileri çözümleyen IoC container’ına sahiptir. Bu özellik yukarıda açıklanmış olan Dependency Injection konusunun temelini oluşturur. Tabii ki yukarıda açıklanandan daha kapsamlılar.
Daha önceki örneklerimizde gördüğümüz @Injectable()
sadece buzdağının görünen basit bir kısmıdır. Providerları sadece @Injectable()
decoratoru ile tanımlamak tek başına yetersiz kalıyor demek doğrudur kısacası.
Kendi oluşturduğunuz providerlarda normal değerleri, sınıfları ya da senkron ya da asenkron factory classlarını kullanabilirsiniz. İşin esası bu 🙂
Opsiyonel Providerlar
Şimdiye kadar tüm providerların mutlaka yüklenmesi gerektiğini söyledik. Ancak bazı durumlarda mutlaka yüklenmeleri gerekmeyebilir. Örneğin, bir sınıfınız var bunun yapacağı iş tamamen alacağı nesneye bağlı olsun. Ancak constructor herhangi bir değer de almamış diyelim. Normalde bu nedenle hatalar alırsınız ancak opsiyonel providerları kullanırsanız bu hataların önüne geçersiniz.
import { Injectable, Optional, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}
Providerların Kayıt Edilmesi
Providerları modül içerisinde çağırmadığınız sürece kullanamazsınız. Bunları kullanabilmek için kayıt etme işlemi şöyle olmalıdır;
ogrenciler.module.ts
import { Module } from '@nestjs/common';
import OgrencilerController from './ogrenciler/ogrenciler.controller';
import OgrencilerService from './ogrenciler/ogrenciler.service';
@Module({
controllers: [OgrencilerController],
providers: [OgrencilerService],
})
export class OgrencilerModule {}
Evet bu kadar. Bu arada bu module gördüğünüz gibi app.module
dosyasından tamamen farklı bir modül. Nest’in birden fazla modül ile çalışmaya imkan tanıdığını biliyorsunuz zaten.
Hepsi bu kadar. Okuduğunuz için teşekkürler. Hatalı, eksik bir bölüm varsa lütfen belirtiniz.