# TREK Server Architecture: NestJS and Express Implementation Guide

> Explore TREK server architecture. This NestJS and Express guide details TypeScript controllers, Express middleware, API serving, static assets, and WebSocket connections.

- Repository: [Maurice/TREK](https://github.com/mauriceboe/TREK)
- Tags: architecture
- Published: 2026-06-27

---

**TREK is built as a single NestJS application running on an Express adapter, combining TypeScript controllers with classic Express middleware to serve APIs, static assets, and real-time WebSocket connections.**

The TREK server architecture powers an open-source travel planning application built by `mauriceboe/TREK`. This monolithic yet modular design leverages NestJS v11 as its core framework while utilizing Express for low-level HTTP handling, creating a hybrid approach that supports both modern dependency injection and traditional middleware patterns.

## Technology Stack and Core Dependencies

TREK runs on **Node.js v22** with a carefully selected stack optimized for type safety and performance:

- **NestJS v11**: The core framework providing decorators, dependency injection, and modular architecture
- **Express**: The underlying HTTP server accessed via `ExpressAdapter` in [`server/src/bootstrap.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/bootstrap.ts)
- **SQLite**: Embedded database using `better-sqlite3` with the main data file at `data/travel.db`
- **WebSocket**: Real-time collaboration via the `ws` library attached to the same HTTP server
- **Authentication**: JWT sessions, OIDC SSO, WebAuthn (Passkeys), and TOTP 2FA
- **MCP**: Model-Context-Protocol server implementing OAuth 2.1 for AI tool integration

The front-end utilizes React 19 with Vite, while the server focuses exclusively on API delivery and static asset management.

## Bootstrap and Application Initialization

The server entry point in [`server/src/bootstrap.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/bootstrap.ts) orchestrates the entire startup sequence through the `buildApp()` function. This async factory creates the Nest application using an Express adapter, then layers middleware in a specific order before initializing controllers.

```typescript
// server/src/bootstrap.ts
export async function buildApp(): Promise<INestApplication> {
  const app = await NestFactory.create(AppModule, new ExpressAdapter());
  const instance = app.getHttpAdapter().getInstance();

  // 1️⃣ Global middleware (CSP, CORS, HSTS, logging, …)
  applyGlobalMiddleware(instance, { bodyParser: false });

  // 2️⃣ Static uploads (avatars, covers, journey photos)
  applyPlatformUploads(instance);

  // 3️⃣ Transport routes (health, OAuth/MCP, well‑known metadata)
  applyPlatformTransport(instance);

  // 4️⃣ Static client assets (served only in production)
  applyPlatformStatic(instance);

  // 5️⃣ All Nest controllers are finally registered
  await app.init();
  return app;
}

```

The ordering is critical. Express routes that must be reachable before Nest's router—such as static files and health checks—are mounted **prior** to `await app.init()`. This ensures direct file serving and OAuth endpoints short-circuit before reaching Nest's controller routing logic.

## Module Hierarchy and Domain Structure

The root `AppModule` in [`server/src/nest/app.module.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/app.module.ts) imports domain-specific modules and registers global filters and interceptors that apply across the entire API surface.

```typescript
// server/src/nest/app.module.ts
@Module({
  imports: [
    DatabaseModule,
    WeatherModule,
    AirportsModule,
    ConfigModule,
    SystemNoticesModule,
    MapsModule,
    AuthModule,
    OidcModule,
    OauthModule,
    AdminModule,
    AddonsModule,
    BookingImportModule,
  ],
  controllers: [HealthController],
  providers: [
    HealthService,
    { provide: APP_FILTER, useClass: TrekExceptionFilter },
    { provide: APP_FILTER, useClass: SpaFallbackFilter },
    { provide: APP_INTERCEPTOR, useClass: IdempotencyInterceptor },
  ],
})
export class AppModule {}

```

Key domain modules live under `server/src/<domain>/` and encapsulate specific business logic:

- **AuthModule**: JWT login, password reset, and session cookie management
- **OidcModule**: OpenID Connect SSO integration for external providers
- **OauthModule**: OAuth 2.1 endpoints supporting the MCP implementation
- **CollabModule**: Real-time chat, notes, and polling features
- **PhotosModule**: Integration with Immich and Synology for photo storage
- **BackupModule**: Scheduled SQLite database backups and restore operations

## Express Platform Routes and Static Handling

Static asset serving and transport endpoints are defined in [`server/src/nest/platform/platform.routes.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/platform/platform.routes.ts), separate from Nest controllers. These Express routes handle infrastructure concerns:

**applyPlatformUploads**: Serves `/uploads/avatars`, `/uploads/covers`, and `/uploads/journey` as public static directories. Photo downloads at `/uploads/photos/:filename` are guarded by JWT or share-token validation in [`server/src/middleware/auth.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/middleware/auth.ts).

**applyPlatformTransport**: Registers critical infrastructure endpoints including:
- `/api/health` health checks
- OIDC and MCP well-known metadata at `/.well-known/*`
- OAuth 2.1 authorization and registration handlers
- `/mcp` endpoint supporting POST, GET, and DELETE methods for the Model-Context-Protocol

**applyPlatformStatic**: In production environments, serves the built React client from the `public/` directory with `no-store` cache headers on [`index.html`](https://github.com/mauriceboe/TREK/blob/main/index.html) to prevent SPA routing issues.

## Real-Time Collaboration via WebSocket

TREK implements real-time synchronization through a WebSocket server defined in [`server/src/websocket.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/websocket.ts). The `ws` library attaches to the same HTTP server created by Nest's Express adapter, allowing authenticated socket connections to broadcast trip updates instantly to all participants.

This design avoids the overhead of separate socket servers while maintaining integration with Nest's authentication middleware. Socket connections validate JWT tokens during the handshake process, ensuring only authorized trip participants receive collaborative updates.

## Authentication and Security Layers

The TREK server architecture implements defense in depth through multiple authentication strategies:

- **JWT Sessions**: Signed with the `ENCRYPTION_KEY` environment variable, stored in HTTP-only cookies
- **TOTP 2FA**: Time-based one-time passwords with backup code recovery
- **WebAuthn Passkeys**: Passwordless authentication toggleable via admin settings
- **OIDC SSO**: Configurable providers including Google, Apple, and Keycloak through `OIDC_*` environment variables
- **Global Middleware**: Helmet-managed CSP headers, CORS configuration, HSTS enforcement, and request logging applied via `applyGlobalMiddleware()` in the bootstrap sequence

The MCP (Model-Context-Protocol) endpoints under `/mcp` reuse the same JWT/refresh-token flow, ensuring consistent security across both human and AI client access.

## Data Layer and Encryption

**SQLite** serves as the single source of truth, managed through `DatabaseModule` in [`server/src/database/database.module.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/database/database.module.ts). The module provides a singleton database instance wrapping `better-sqlite3` for synchronous query execution.

Sensitive data protection relies on the **EncryptionKey** system, which encrypts at-rest secrets including API keys and MFA seeds. The [`migrate-encryption.ts`](https://github.com/mauriceboe/TREK/blob/main/migrate-encryption.ts) script handles key rotation without service interruption.

## Add-On System and Feature Toggling

TREK supports runtime feature toggling through an add-on system checked via `isAddonEnabled(ADDON_IDS.<NAME>)`. Admin users enable features like MCP, Atlas, or Vacay through the admin UI, with states stored in the database.

This architecture prevents feature fingerprinting by ensuring disabled routes return 404 errors rather than exposing authentication requirements. The design keeps the core API stable while allowing optional modules to load dynamically without code changes.

## Deployment and Startup Flow

The complete startup sequence follows this pattern:

1. **Bootstrap**: `buildApp()` creates the Nest application with Express adapter
2. **Middleware**: Global security headers and logging applied to Express instance
3. **Platform Routes**: Upload directories, health checks, and OAuth endpoints mounted
4. **Static Assets**: Production client files registered
5. **Controller Registration**: `await app.init()` activates all Nest controllers
6. **WebSocket Attachment**: Real-time server binds to the HTTP listener

Deployment utilizes Docker with the `ENCRYPTION_KEY` environment variable:

```bash
ENCRYPTION_KEY=$(openssl rand -hex 32) docker run -d -p 3000:3000 \
  -e ENCRYPTION_KEY=$ENCRYPTION_KEY \
  -v ./data:/app/data -v ./uploads:/app/uploads mauriceboe/trek

```

## Extending the Architecture

Adding new functionality requires minimal changes to the core infrastructure. Create a module following Nest conventions:

```typescript
// src/myfeature/myfeature.module.ts
import { Module } from '@nestjs/common';
import { MyFeatureController } from './myfeature.controller';
import { MyFeatureService } from './myfeature.service';

@Module({
  controllers: [MyFeatureController],
  providers: [MyFeatureService],
})
export class MyFeatureModule {}

```

Then import it in [`server/src/nest/app.module.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/app.module.ts) to automatically register the new API routes within the unified Nest application without modifying Express configuration.

## Summary

- **TREK server architecture** combines NestJS v11 with an Express adapter, creating a hybrid monolith that leverages modern dependency injection while maintaining traditional middleware capabilities
- **Bootstrap sequence** in [`server/src/bootstrap.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/bootstrap.ts) applies Express routes for static files and OAuth endpoints before initializing Nest controllers via `app.init()`
- **Domain modules** like `AuthModule`, `CollabModule`, and `PhotosModule` encapsulate business logic under [`server/src/nest/app.module.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/app.module.ts)
- **Real-time features** use the `ws` library attached to the same HTTP server, with authentication handled in [`server/src/websocket.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/websocket.ts)
- **Security** implements layered authentication including JWT, OIDC, WebAuthn, and TOTP, with global middleware enforcing CSP and CORS policies
- **Data persistence** relies on SQLite through `DatabaseModule`, with at-rest encryption managed via the `EncryptionKey` provider

## Frequently Asked Questions

### What core technologies power the TREK server architecture?

The TREK server is built as a **NestJS v11** application running on **Node.js v22**, utilizing an **Express adapter** for HTTP handling. It uses **SQLite** as its primary database via the `better-sqlite3` library, and implements real-time collaboration through the **WebSocket (`ws`)** library attached to the same server instance.

### How does TREK handle static file serving alongside API routes?

Static files are served through **Express middleware** registered in [`server/src/nest/platform/platform.routes.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/platform/platform.routes.ts) before the Nest application initializes. The `applyPlatformUploads()` and `applyPlatformStatic()` functions mount directories like `/uploads/avatars` and the production client build **prior** to `await app.init()`, ensuring these routes short-circuit before reaching Nest's controller routing.

### What authentication methods does the TREK server support?

The architecture supports **JWT sessions** with HTTP-only cookies, **TOTP 2FA** with backup codes, **WebAuthn Passkeys** for passwordless login, and **OIDC SSO** integration for external providers like Google and Keycloak. All methods are implemented through modular providers in `AuthModule` and `OidcModule`, with validation logic centralized in [`server/src/middleware/auth.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/middleware/auth.ts).

### How does the add-on system work in TREK's modular architecture?

Add-ons are runtime features toggled via the admin UI and stored in the SQLite database. The server checks `isAddonEnabled(ADDON_IDS.<NAME>)` before exposing routes, ensuring disabled features return 404 responses. This design keeps the core API stable while allowing optional modules like MCP or Atlas to load dynamically without requiring application restarts or code changes.