-
Notifications
You must be signed in to change notification settings - Fork 2
1.4 Server
What you will find on this page π¦
- Folder Structure - High-level folder structure of NestJS server explained
- Module Overview - Learning Goals, Sessions, Blocks, LLM, Database
- Basic Request Flow - from HTTP request to HTTP response
- Zod Schemas - single source of truth for types across server/client
The server is a NestJS 11 application written in TypeScript.
server/src/
βββ main.ts # Bootstrap
βββ app.module.ts # Root module
β
βββ common/ # Cross-cutting concerns: logging decorators/interceptors/utils, health check
βββ config/ # App config, logging config, env var validation (Joi)
β
βββ domain/ # Framework-agnostic domain knowledge and shared type definitions
β βββ didactical-frameworks/ # Bloom's, SOLO and AVIVA definitions
β βββ schemas/ # Zod schemas - single source of truth for types
β βββ base/ # Defines base schemas (learning-goal, session, all block types, ...)
β βββ dto/ # Defines 2 DTOs for every API endpoint (1 request DTO + 1 response DTO)
β βββ llm-parser/ # Defines schemas for structured LLM output parsing
β
βββ modules/ # Feature modules: each owns its routes, controllers, services
βββ learning-goals/ # Generate learning goals + easier alternatives
βββ sessions/ # Handles session lifecycle: create, get, update, delete, continue, feedback
βββ blocks/ # Handles block generation (sequences, summary), chat, answer submission
βββ shared/
βββ database/ # Prisma repositories (sessions, blocks)
βββ llm/ # LLM integration
βββ llm.service.ts # Anthropic SDK wrapper - callClaude()
βββ llm.parser.ts # Zod-based structured output parser with retry
βββ chains/ # One chain per use case (prompt + llm.service call + parse)
βββ prompts/ # One prompt per chain
| Module | Responsibility |
|---|---|
LearningGoalsModule |
Handles generation of learning goals for new / easier session |
SessionsModule |
Handles session lifecycle: create, navigate, continue logic, feedback |
BlocksModule |
Handles block generation (sequences, summary), chat responses, answer evaluation |
LlmModule |
Handles LLM integration |
DatabaseModule |
Handles database interaction via repository pattern - all Prisma access is encapsulated in repositories so services never call Prisma directly, keeping DB logic in one place |
From HTTP request to HTTP response - here is what happens:
HTTP Request
β Controller (validates request DTO, calls service)
β Service (handles business logic, interacts with DB via repositories)
β Chain (builds LLM call: selects prompt, calls LLM via service, parses output)
β Prompt (constructs the final prompt string)
β LlmService (calls Anthropic SDK)
β LlmParser (validates structured JSON response against Zod schema, retries on failure)
β Service (again handles business logic, interacts with DB via repositories, maps result to Response DTO)
β Controller (validates response DTO)
β HTTP Response
server/src/domain/schemas/ is the single source of truth for all types used in ExplAIner across server and client. If you want to add a new schema or adjust an existing one, do it there! :) nestjs-zod converts the Zod schemas into NestJS DTOs and OpenAPI decorators, which @nestjs/swagger exposes as an OpenAPI spec. The client then uses openapi-typescript to generate api.types.ts from that spec.
Sounds complex? It's not, Owlbert promises! π¦ Here's how to add or change a schema:
Case 1: Server-Side Only
Use this when the schema change does not affect the API (e.g. changing a base schema or LLM parser schema that is not exposed to the client).
1. Add or change the Zod schema in server/src/domain/schemas/
2. Fix any TypeScript errors on the server
Case 2: Schema Exposed via API (affects client)
Use this when the change affects data sent to/from the client - i.e. a DTO in
schemas/dto/changes.
1. Add or change the Zod schema in server/src/domain/schemas/
- Update the corresponding DTOs in
schemas/dto/- one request DTO and one response DTO per endpoint
2. Start the server, then regenerate client types:
cd client
npm run generate:api-typesβ writes client/src/types/generated/api.types.ts (do not edit by hand)
3. (Optional) Add a re-export from client/src/types/domain/ for simpler usage
4. TypeScript will surface errors wherever the changed type is used - fix all of them:
// TypeScript error: Property 'newField' is missing
const session: Session = { ... };
Simply click here to checkout ExplAIner yourself! π¦
Welcome
System Architecture
Contributor Guide