The API server for PRESIM — a scenario-based public speaking rehearsal platform that places users inside a lifelike virtual environment with responsive AI audience behaviour.
- NestJS + TypeScript (strict mode)
- PostgreSQL — relational database
- TypeORM — ORM with migrations
- ULID — sortable unique identifiers for primary keys
- Swagger — auto-generated API documentation
- Jest — unit and e2e testing
- Helmet — security headers
- class-validator — request validation
src/
├── common/
│ └── filters/ # Global exception filter
├── config/
│ └── database.config.ts # TypeORM async configuration
├── database/
│ ├── data-source.ts # Standalone DataSource for TypeORM CLI
│ ├── migrations/ # Database migrations
│ └── seeds/ # Seed data scripts
├── health/
│ ├── health.controller.ts
│ └── health.module.ts
├── scenarios/
│ ├── dto/ # Request/response DTOs with validation
│ ├── entities/ # TypeORM entity definitions
│ ├── scenarios.controller.ts
│ ├── scenarios.service.ts
│ └── scenarios.module.ts
├── app.module.ts
└── main.ts
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/scenarios |
List all scenarios (optional ?category= filter) |
GET |
/api/v1/scenarios/:slug |
Get a single scenario by slug |
GET |
/api/v1/health |
Health check |
GET |
/api/docs |
Swagger API documentation |
- Node.js 22+
- PostgreSQL (pgAdmin or psql)
- Create the database:
CREATE DATABASE presim;- Copy environment variables:
cp .env.example .env-
Update
.envwith your PostgreSQL credentials. -
Run migrations:
npm run migration:run- Seed data:
npm run seed# Install dependencies
npm install
# Development (watch mode)
npm run start:dev
# Production
npm run build
npm run start:prodThe API will be available at http://localhost:3000. Swagger docs at http://localhost:3000/api/docs.
| Variable | Description | Default |
|---|---|---|
NODE_ENV |
Environment mode | development |
API_PORT |
Server port | 3000 |
DATABASE_HOST |
PostgreSQL host | localhost |
DATABASE_PORT |
PostgreSQL port | 5432 |
DATABASE_NAME |
Database name | presim |
DATABASE_USER |
Database user | postgres |
DATABASE_PASSWORD |
Database password | postgres |
CORS_ORIGINS |
Allowed origins (comma-separated) | http://localhost:5173 |
| Command | Description |
|---|---|
npm run start:dev |
Start in development watch mode |
npm run start:prod |
Start production build |
npm run build |
Compile TypeScript |
npm run test |
Run unit tests |
npm run test:watch |
Run tests in watch mode |
npm run test:cov |
Run tests with coverage report |
npm run test:e2e |
Run end-to-end tests |
npm run migration:run |
Run pending database migrations |
npm run migration:revert |
Revert last migration |
npm run seed |
Seed the database with scenario data |
npm run lint |
Lint with ESLint |
# Unit tests
npm run test
# With coverage
npm run test:cov
# E2E tests
npm run test:e2e- Scenarios Service — findAll, findAll with category filter, findOne, 404 handling
- Scenarios Controller — GET /scenarios, GET /scenarios/:slug, query passthrough, error propagation
- Health Controller — status response with timestamp
- Global Exception Filter — HttpException handling, unknown error handling, response shape
- Helmet — security headers (X-Content-Type-Options, X-Frame-Options, etc.)
- CORS — restricted to configured origins
- Rate Limiting — 100 requests per minute per IP
- Input Validation — class-validator with whitelist (strips unknown fields)
- SQL Injection Prevention — TypeORM parameterized queries
- No Secrets in Code — all configuration via environment variables
The scenarios table uses ULID primary keys and JSONB columns for nested data:
| Column | Type | Notes |
|---|---|---|
id |
varchar(26) |
ULID primary key |
slug |
varchar(64) |
Unique human-readable identifier |
audience_roles |
jsonb |
Array of role objects |
behaviour_rules |
jsonb |
Array of rule objects |
audience_size_range |
jsonb |
[min, max] tuple |
duration_options |
jsonb |
Array of duration integers |
*_id columns |
varchar |
FK references to future tables |