src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { LoggerModule } from 'nestjs-pino';
import { ScheduleModule } from '@nestjs/schedule';
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
import { AuditLogInterceptor } from './common/interceptors/audit-log.interceptor';
import { TenantContextInterceptor } from './common/interceptors/tenant-context.interceptor';
import { TenantHeaderInterceptor } from './common/interceptors/tenant-header.interceptor';
import { TenantLookupService } from './common/services/tenant-lookup.service';
import configuration from './config/configuration';
import { PrismaModule } from './prisma/prisma.module';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { ClientsModule } from './clients/clients.module';
import { OrdersModule } from './orders/orders.module';
import { VehiclesModule } from './vehicles/vehicles.module';
import { DriversModule } from './drivers/drivers.module';
import { TripsModule } from './trips/trips.module';
import { AlertsModule } from './alerts/alerts.module';
import { AnalyticsModule } from './analytics/analytics.module';
import { TrackingModule } from './tracking/tracking.module';
import { WebhookModule } from './webhooks/webhook.module';
import { HealthModule } from './health/health.module';
import { NotificationModule } from './notifications/notification.module';
import { StorageModule } from './storage/storage.module';
import { AiModule } from './ai/ai.module';
import { BillingModule } from './billing/billing.module';
import { TransportersModule } from './transporters/transporters.module';
import { ZonesModule } from './zones/zones.module';
import { LanesModule } from './lanes/lanes.module';
import { LoadingBaysModule } from './loading-bays/loading-bays.module';
import { MessagesModule } from './messages/messages.module';
import { ClientPortalModule } from './client-portal/client-portal.module';
@Module({
imports: [
// Global configuration from .env
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '../../.env',
load: [configuration],
}),
// Pino structured logging
LoggerModule.forRoot({
pinoHttp: {
transport:
process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty', options: { colorize: true } }
: undefined,
level: process.env.NODE_ENV !== 'production' ? 'debug' : 'info',
},
}),
// Rate limiting
ThrottlerModule.forRoot([
{ name: 'short', ttl: 1000, limit: 3 }, // 3 requests per second
{ name: 'medium', ttl: 10000, limit: 20 }, // 20 per 10 seconds
{ name: 'long', ttl: 60000, limit: 100 }, // 100 per minute
]),
// Cron / scheduled tasks
ScheduleModule.forRoot(),
// Core modules
PrismaModule,
NotificationModule,
AiModule,
StorageModule,
// Health check (no auth required)
HealthModule,
// Feature modules
AuthModule,
UsersModule,
ClientsModule,
OrdersModule,
VehiclesModule,
DriversModule,
TripsModule,
AlertsModule,
AnalyticsModule,
TrackingModule,
WebhookModule,
BillingModule,
TransportersModule,
ZonesModule,
LanesModule,
LoadingBaysModule,
MessagesModule,
ClientPortalModule,
],
controllers: [],
providers: [
{ provide: APP_GUARD, useClass: ThrottlerGuard },
TenantLookupService,
// Order matters: NestJS runs APP_INTERCEPTOR providers in registration
// order, with the FIRST registered being the OUTERMOST wrapper.
//
// 1) TenantHeaderInterceptor — runs first so a 403 mismatch aborts
// before any RLS tx is opened. Guards have already run by this point,
// so req.user is set for JWT-protected routes.
// 2) TenantContextInterceptor — opens the $transaction and SETs the
// app.current_org_id GUC for RLS.
// 3) AuditLogInterceptor — logs the request inside the tenant tx.
{ provide: APP_INTERCEPTOR, useClass: TenantHeaderInterceptor },
{ provide: APP_INTERCEPTOR, useClass: TenantContextInterceptor },
{ provide: APP_INTERCEPTOR, useClass: AuditLogInterceptor },
],
})
export class AppModule {}