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.tsruns class-validator on startup and throws if config is bad. - Testability: easy to mock the service in unit tests.
Required and optional
| Variable | Required | Description |
|---|---|---|
AUTH_SECRET | yes | better-auth signing secret. Rotate carefully; old sessions invalidate. |
AUTH_URL | yes | Public base URL of this service (e.g. https://api.fleapo.ai) |
DATABASE_URL | one of | Full Postgres URL |
DATABASE_HOST/PORT/USERNAME/PASSWORD/DATABASE | one of | Individual DB params |
DATABASE_CERT | no | RDS CA cert (PEM string). If absent, rejectUnauthorized: false (dev). |
PORT | no | HTTP port, default 3000 |
NODE_ENV | no | development | production | test | staging |
POSTHOG_API_KEY | no | Audit log batching off if absent |
POSTHOG_HOST | no | https://app.posthog.com |
LOG_BATCH_SIZE | no | 100 |
LOG_BATCH_INTERVAL_MS | no | 30000 |
FRONTEND_URL | no | CORS allow-list (comma-separated). Default http://localhost:8080. Wildcards not allowed with credentials: true. |
ADMIN_EMAIL | no | Seed admin, default admin@example.com |
ADMIN_PASSWORD | no | Seed admin password, default ChangeMe123! — change in prod |
ADMIN_NAME | no | Seed admin display name, default Platform Admin |
STRIPE_SECRET_KEY | no | If absent, paid tier creation throws SERVICE_UNAVAILABLE |
STRIPE_WEBHOOK_SECRET | no | Required to verify Stripe webhook signatures |
GIT_SHA | no | Injected by Docker build; surfaced at /health |
RELEASE_VERSION | no | Same |
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:
- Adding a
@IsString()/@IsOptional()property to the env validation class. - Adding a getter on
AppConfigServicethat reads from it. - 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
.envor.env.productionto git. - Rotate
AUTH_SECRETonly during scheduled maintenance — it invalidates all sessions.