Skip to content
← Back to blog
NestJSAngularSaaSTurkishRehberTutorial

NestJS ve Angular ile SaaS Geliştirme: Kapsamlı Rehber 2026

Firas Sayah·July 18, 2026·8 min read

Neden NestJS + Angular ile SaaS Geliştirmelisiniz?

Gecen yil bir freelance projede kimlik dogrulama, odeme sistemi ve coklu kiraciligi sifirdan yazdim. Dört ay sonra, müsterim hâlâ sadece giris yapip fatura görebiliyordu. Ürüne özgü tek bir özellik bile kodlamamistim. O projeden sonra anladim: SaaS gelistirmede asil mesele "nasil yazilir" degil, "neyi yazmamak gerekir" meselesidir.

Ekranda kod görüntüsü

Türkiye'nin yazılım geliştirme ekosistemi 2026 yılında hızla büyümeye devam ediyor. Girişimlerden büyük şirketlere kadar, SaaS çözümlerine olan talep her geçen gün artıyor. NestJS ve Angular kombinasyonu, kurumsal düzeyde SaaS uygulamaları geliştirmek için en güçlü teknoloji yığınlarından birini oluşturuyor.

Bu kombinasyonun avantajları:

  • TypeScript birliği: Frontend ve backend aynı dili paylaşır, tip güvenliği sağlanır
  • Modüler mimari: Her iki framework de modül tabanlı tasarıma sahiptir
  • Güçlü bağımlılık enjeksiyonu: Test edilebilirlik ve bakım kolaylığı
  • Zengin ekosistem: Geniş kütüphane ve eklenti desteği
  • Kurumsal destek: Angular'ın arkasında Google, NestJS'in arkasında güçlü bir topluluk

Next.js ile karsilastirma yapmak istiyorsaniz, NestJS + Angular vs Next.js detayli karsilastirma yazimiza göz atin.

Proje Yapısı

Nx monorepo kullanarak projeyi organize ediyoruz:

saas-projesi/
├── apps/
│   ├── api/                # NestJS backend
│   │   ├── src/
│   │   │   ├── auth/       # Kimlik doğrulama modülü
│   │   │   ├── billing/    # Ödeme modülü
│   │   │   ├── tenants/    # Kiracı yönetimi
│   │   │   └── users/      # Kullanıcı yönetimi
│   ├── web/                # Angular frontend
│   │   ├── src/
│   │   │   ├── app/
│   │   │   │   ├── auth/
│   │   │   │   ├── dashboard/
│   │   │   │   └── shared/
├── libs/                   # Paylaşılan kütüphaneler
│   ├── shared-types/       # Paylaşılan tip tanımları
│   └── shared-utils/       # Paylaşılan yardımcı fonksiyonlar

Cloudrix SaaS Starter ships with this pre-configured and tested.

Skip weeks of boilerplate — auth, payments, multi-tenancy, and deployment included out of the box.

Try the live demo →

Adım 1: Kimlik Doğrulama Sistemi

JWT tabanlı kimlik doğrulama, her SaaS uygulamasının temelini oluşturur.

Backend Kimlik Doğrulama Servisi

// auth.service.ts
@Injectable()
export class AuthService {
  constructor(
    private readonly usersService: UsersService,
    private readonly jwtService: JwtService,
    private readonly tenantsService: TenantsService,
  ) {}

  async kayitOl(dto: RegisterDto) {
    const mevcutKullanici = await this.usersService.findByEmail(dto.email);

    if (mevcutKullanici) {
      throw new ConflictException('Bu e-posta adresi zaten kayıtlı');
    }

    const sifreHash = await bcrypt.hash(dto.sifre, 12);
    const kullanici = await this.usersService.create({
      email: dto.email,
      password: sifreHash,
      ad: dto.ad,
      soyad: dto.soyad,
    });

    // Yeni kiracı otomatik oluştur
    const tenant = await this.tenantsService.create({
      ad: dto.sirketAdi,
      sahipId: kullanici.id,
      plan: 'ucretsiz',
    });

    return this.tokenOlustur(kullanici, tenant.id);
  }

  async girisYap(dto: LoginDto) {
    const kullanici = await this.usersService.findByEmail(dto.email);

    if (!kullanici || !(await bcrypt.compare(dto.sifre, kullanici.password))) {
      throw new UnauthorizedException('E-posta veya şifre hatalı');
    }

    return this.tokenOlustur(kullanici, kullanici.tenantId);
  }

  private tokenOlustur(kullanici: User, tenantId: string) {
    const payload = {
      sub: kullanici.id,
      email: kullanici.email,
      tenantId,
      rol: kullanici.rol,
    };

    return {
      accessToken: this.jwtService.sign(payload, { expiresIn: '15m' }),
      refreshToken: this.jwtService.sign(payload, { expiresIn: '7d' }),
      kullanici: {
        id: kullanici.id,
        email: kullanici.email,
        ad: kullanici.ad,
      },
    };
  }
}

Frontend Kimlik Doğrulama

// auth.service.ts
@Injectable({ providedIn: 'root' })
export class AuthService {
  private readonly http = inject(HttpClient);
  private readonly router = inject(Router);

  private mevcutKullanici = signal<User | null>(null);
  readonly girisYapildiMi = computed(() => !!this.mevcutKullanici());
  readonly kullanici = this.mevcutKullanici.asReadonly();

  girisYap(bilgiler: { email: string; sifre: string }) {
    return this.http.post<AuthResponse>('/api/auth/login', bilgiler).pipe(
      tap(yanit => {
        localStorage.setItem('accessToken', yanit.accessToken);
        localStorage.setItem('refreshToken', yanit.refreshToken);
        this.mevcutKullanici.set(yanit.kullanici);
        this.router.navigate(['/panel']);
      }),
    );
  }

  cikisYap() {
    localStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
    this.mevcutKullanici.set(null);
    this.router.navigate(['/giris']);
  }
}

Adım 2: Çoklu Kiracı (Multi-Tenant) Mimarisi

SaaS uygulamalarında kiracılar arası veri izolasyonu kritik öneme sahiptir:

// tenant.middleware.ts
@Injectable()
export class TenantMiddleware implements NestMiddleware {
  constructor(
    private readonly tenantsService: TenantsService,
    private readonly jwtService: JwtService,
    private readonly cls: ClsService,
  ) {}

  async use(req: Request, res: Response, next: NextFunction) {
    const token = this.tokenCikar(req);

    if (token) {
      const payload = this.jwtService.verify(token);
      const tenant = await this.tenantsService.findById(payload.tenantId);

      if (!tenant || !tenant.aktif) {
        throw new ForbiddenException('Geçersiz veya devre dışı kiracı');
      }

      this.cls.set('tenantId', tenant.id);
      this.cls.set('plan', tenant.plan);
    }

    next();
  }

  private tokenCikar(req: Request): string | null {
    const yetki = req.headers.authorization;
    return yetki?.startsWith('Bearer ') ? yetki.slice(7) : null;
  }
}

Veritabanı Düzeyinde İzolasyon

// tenant-aware.repository.ts
@Injectable()
export class TenantAwareRepository<T extends TenantEntity> {
  constructor(
    @InjectRepository(T) private readonly repo: Repository<T>,
    private readonly cls: ClsService,
  ) {}

  async tumunuBul(): Promise<T[]> {
    const tenantId = this.cls.get('tenantId');
    return this.repo.find({ where: { tenantId } as any });
  }

  async olustur(veri: Partial<T>): Promise<T> {
    const tenantId = this.cls.get('tenantId');
    const kayit = this.repo.create({
      ...veri,
      tenantId,
    } as any);
    return this.repo.save(kayit);
  }

  async idIleBul(id: string): Promise<T | null> {
    const tenantId = this.cls.get('tenantId');
    return this.repo.findOne({
      where: { id, tenantId } as any,
    });
  }
}

Adım 3: Ödeme Entegrasyonu

Stripe entegrasyonu ile abonelik yönetimi. Daha detayli Stripe rehberi için Stripe Subscriptions tam rehber yazisina bakin.

Tüm bu kodu kendiniz yazmak zorunda degilsiniz. SaaS Starter, kimlik doğrulama, ödeme ve çoklu kiracılığı 55'ten fazla test ile birlikte içerir. Ücretsiz Lite sürümünü deneyin →

Dizüstü bilgisayarda kodlama

// billing.service.ts
@Injectable()
export class BillingService {
  private stripe: Stripe;

  constructor(private readonly configService: ConfigService) {
    this.stripe = new Stripe(this.configService.get('STRIPE_SECRET_KEY'));
  }

  async odemeOturumuOlustur(tenantId: string, planId: string) {
    const fiyatlar = {
      baslangic: 'price_starter',
      profesyonel: 'price_pro',
      kurumsal: 'price_enterprise',
    };

    return this.stripe.checkout.sessions.create({
      mode: 'subscription',
      payment_method_types: ['card'],
      line_items: [{ price: fiyatlar[planId], quantity: 1 }],
      success_url: `${this.configService.get('APP_URL')}/odeme/basarili`,
      cancel_url: `${this.configService.get('APP_URL')}/odeme/iptal`,
      metadata: { tenantId, planId },
    });
  }

  async webhookIsle(olay: Stripe.Event) {
    switch (olay.type) {
      case 'customer.subscription.created':
        await this.aboneligiEtkinlestir(olay.data.object);
        break;
      case 'customer.subscription.deleted':
        await this.aboneligiDevreDisibirak(olay.data.object);
        break;
      case 'invoice.payment_failed':
        await this.basarisizOdemeIsle(olay.data.object);
        break;
    }
  }
}

Adım 4: Rol Tabanlı Erişim Kontrolü (RBAC)

// roller.guard.ts
@Injectable()
export class RollerGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const gerekliRoller = this.reflector.getAllAndOverride<Rol[]>('roller', [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!gerekliRoller) return true;

    const { user } = context.switchToHttp().getRequest();
    return gerekliRoller.includes(user.rol);
  }
}

// Kullanım örneği
@Controller('yonetim')
@UseGuards(JwtAuthGuard, RollerGuard)
export class YonetimController {
  @Get('kullanicilar')
  @Roller(Rol.ADMIN, Rol.SUPER_ADMIN)
  async kullanicilariGetir() {
    return this.usersService.tumunuBul();
  }

  @Delete('kullanicilar/:id')
  @Roller(Rol.SUPER_ADMIN)
  async kullaniciSil(@Param('id') id: string) {
    return this.usersService.sil(id);
  }
}

Adım 5: Angular Dashboard

Angular Signals kullanarak modern bir yönetim paneli oluşturuyoruz:

// dashboard.component.ts
@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [IstatistikKartiComponent, GrafikComponent, CurrencyPipe],
  template: `
    <h1 class="text-2xl font-bold mb-6">Yönetim Paneli</h1>

    <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
      <app-istatistik-karti
        baslik="Aylık Gelir"
        [deger]="istatistikler()?.aylikGelir | currency:'TRY':'symbol':'1.0-0'"
        ikon="trending_up"
      />
      <app-istatistik-karti
        baslik="Aktif Kullanıcılar"
        [deger]="istatistikler()?.aktifKullanicilar"
        ikon="people"
      />
      <app-istatistik-karti
        baslik="Dönüşüm Oranı"
        [deger]="istatistikler()?.donusumOrani + '%'"
        ikon="analytics"
      />
      <app-istatistik-karti
        baslik="Kayıp Oranı"
        [deger]="istatistikler()?.kayipOrani + '%'"
        ikon="warning"
      />
    </div>

    <app-grafik
      baslik="Gelir Trendi"
      [veriler]="gelirVerileri()"
      tip="line"
    />
  `,
})
export class DashboardComponent {
  private readonly analyticsService = inject(AnalyticsService);

  istatistikler = toSignal(this.analyticsService.getIstatistikler());
  gelirVerileri = toSignal(this.analyticsService.getGelirVerileri());
}

Adım 6: Docker ile Dağıtım

# docker-compose.prod.yml
services:
  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
      target: production
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy

  web:
    build:
      context: .
      dockerfile: apps/web/Dockerfile
    ports:
      - "80:80"
      - "443:443"

  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 10s

  redis:
    image: redis:7-alpine

volumes:
  pgdata:

Sıfırdan Geliştirmek mi, Boilerplate Kullanmak mı?

Bir SaaS uygulamasını sıfırdan geliştirmek, yalnızca temel özellikler (kimlik doğrulama, ödeme, çoklu kiracı, yönetim paneli) için bile 4-6 ay sürer. Bu süreyi kısaltmak ve ürününüzün kendine özgü özelliklerine odaklanmak istiyorsanız, hazır bir boilerplate kullanmak mantıklıdır.

SaaS Starter, tüm bu özellikleri içeren NestJS + Angular boilerplate'idir:

  • Ücretsiz Lite sürümü ile temel özellikleri keşfedin
  • Pro ($149): Kimlik doğrulama + ödeme + temel panel
  • Business ($249): Çoklu kiracı + RBAC + gelişmiş özellikler
  • Enterprise ($399): Tüm özellikler + öncelikli destek

Sonuç: Hemen Başlayın

NestJS ve Angular kombinasyonu, Türkiye'deki geliştiriciler için kurumsal düzeyde SaaS uygulamaları geliştirmek adına mükemmel bir seçimdir. TypeScript'in tip güvenliği, modüler mimari ve zengin ekosistem sayesinde ölçeklenebilir ve bakımı kolay uygulamalar oluşturabilirsiniz.

Dashboard UI ekranı

Geliştirme sürecinizi hızlandırmak için SaaS Starter canlı demo sayfasını ziyaret edin. Ücretsiz Lite sürümüyle başlayın ve projeniz büyüdükçe yükseltin.

Fiyatlandırma:

  • Lite (Ücretsiz) — Temel mimariyi keşfedin
  • Pro ($149) — Kimlik doğrulama + ödeme + panel
  • Business ($249) — Çoklu kiracı + RBAC + gelişmiş özellikler
  • Enterprise ($399) — Tüm özellikler + öncelikli destek

Fiyatları görüntüle | Canlı demoyu deneyin | Ücretsiz Lite sürümünü indirin

İlgili yazılar: Angular yönetim paneli rehberi | NestJS REST API eğitimi | AWS dağıtım rehberi

F

Firas Sayah

Senior Software Engineer

Full-stack developer with 5+ years building production SaaS applications with NestJS and Angular.