Skip to main content

Config & env vars

AppConfigService is the single typed source of truth. Never read process.env directly inside a NestJS module — only main.ts, migrate.ts, seed.ts are exempt.

Why centralized

  • Type-safety: config.databaseUrl: string, config.posthogApiKey: string | undefined.
  • Validation: env.validation.ts runs class-validator on startup and throws if config is bad.
  • Testability: easy to mock the service in unit tests.

Required and optional

VariableRequiredDescription
AUTH_SECRETyesbetter-auth signing secret. Rotate carefully; old sessions invalidate.
AUTH_URLyesPublic base URL of this service (e.g. https://api.fleapo.ai)
DATABASE_URLone ofFull Postgres URL
DATABASE_HOST/PORT/USERNAME/PASSWORD/DATABASEone ofIndividual DB params
DATABASE_CERTnoRDS CA cert (PEM string). If absent, rejectUnauthorized: false (dev).
PORTnoHTTP port, default 3000
NODE_ENVnodevelopment | production | test | staging
POSTHOG_API_KEYnoAudit log batching off if absent
POSTHOG_HOSTnohttps://app.posthog.com
LOG_BATCH_SIZEno100
LOG_BATCH_INTERVAL_MSno30000
FRONTEND_URLnoCORS allow-list (comma-separated). Default http://localhost:8080. Wildcards not allowed with credentials: true.
ADMIN_EMAILnoSeed admin, default admin@example.com
ADMIN_PASSWORDnoSeed admin password, default ChangeMe123! — change in prod
ADMIN_NAMEnoSeed admin display name, default Platform Admin
STRIPE_SECRET_KEYnoIf absent, paid tier creation throws SERVICE_UNAVAILABLE
STRIPE_WEBHOOK_SECRETnoRequired to verify Stripe webhook signatures
GIT_SHAnoInjected by Docker build; surfaced at /health
RELEASE_VERSIONnoSame

Reading from a NestJS module

@Injectable()
export class MyService {
constructor(private readonly config: AppConfigService) {}

someMethod() {
return fetch(`${this.config.authUrl}/...`);
}
}

env.validation.ts

@Module({ imports: [ConfigModule.forRoot({ validate, isGlobal: true })] })validate runs class-validator on the parsed env object at startup. If anything fails, the process exits before listening.

Add new vars by:

  1. Adding a @IsString() / @IsOptional() property to the env validation class.
  2. Adding a getter on AppConfigService that reads from it.
  3. Updating this docs table.

CORS

app.enableCors({
origin: config.frontendUrl.split(',').map(s => s.trim()),
credentials: true,
});

credentials: true + wildcard * origin is rejected by every modern browser. The frontend URLs must be enumerated explicitly.

Secrets

In prod:

  • AWS Secrets Manager → injected into the ECS task definition as env vars.
  • Never commit .env or .env.production to git.
  • Rotate AUTH_SECRET only during scheduled maintenance — it invalidates all sessions.