in JavaScript, NestJS, Programming, TypeScript

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.

Kaynaklar