MindFlow is built on a classic three-tier client-server architecture: a React/Next.js presentation layer, a NestJS REST API tier, and a PostgreSQL persistence tier. All three live together inside a single npm workspace monorepo, which means domain interfaces are defined once in a shared package and consumed by both the frontend and the backend without duplication. In development and CI, Docker Compose orchestrates the three services; in production each tier is deployed independently to the provider of choice. The sections below describe how each layer is structured, how the tiers communicate, and how the database schema is organised.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/J0S3-LEON/pf_pruebasAseguramientoCalid_SDD/llms.txt
Use this file to discover all available pages before exploring further.
Monorepo layout
The repository root is the npm workspace root. Workspaces are declared inpackage.json as apps/* and packages/*:
apps/frontend
Next.js 14 + TypeScript. Uses the App Router for server and client components, Auth.js for session management, SWR for data fetching, and Shadcn/ui + Tailwind CSS for the component library. Third-party animation libraries are isolated behind the Wrapper Pattern so that the core UI always renders without them.
apps/backend
NestJS 10 + Node.js. Exposes a versioned REST API under
/api/v1/. Uses Prisma 7 as the ORM, JWT for stateless authentication, Helmet for HTTP security headers, and fast-check for property-based testing. The main.ts bootstrap includes a 5-retry connection loop for PostgreSQL.packages/shared
@mindflow/shared. A lightweight TypeScript package containing the domain interfaces (
Session, Task, FatigueRecord, MicroObjective, etc.) and utility types shared between the frontend and backend. Must be built before either app — npm run build --workspace @mindflow/shared.compose.yml
Docker Compose (development only). Defines three services —
db (PostgreSQL 18 Alpine), backend, and frontend — on an isolated internal network. Only the frontend exposes a port externally. The mindflow_pgdata named volume persists database data across container restarts.System architecture
The data flow through MindFlow follows a clear, linear path. The student’s browser communicates exclusively with the Next.js frontend over HTTPS. The frontend in turn communicates with the NestJS API over HTTP on the internal Docker network (in production, the frontend proxies all/api/v1 requests through Next.js rewrites so the backend is never directly exposed to the public internet).
Request flow — EMA session example:
- The student clicks “Start EMA Session” in the browser.
- The Next.js frontend sends
POST /api/v1/sessionswith the student’s JWT in theAuthorization: Bearerheader. - The NestJS
API_Gateway(guards + interceptors) validates the JWT, then delegates to theSessionModule. - The
SessionModulecallsDB_Writer(the Prisma repository layer) to persist a newSessionrecord withis_active: true. - The
API_Gatewaywraps the response in the standard envelope{ data, error: null, status: 201 }and returns it to the frontend. - The student submits a
Fatigue_Score. If the score is ≥ 4, theSessionModuleinvokes theTaskDecomposerModule, which calls an external LLM service over HTTP. - The LLM returns 2–7 micro-objectives. The
DB_Writerpersists them, and the wrapped response flows back to the frontend dashboard.
Every component in the backend — including the
GlobalExceptionFilter for errors — wraps its output in the same JSON envelope: { "data": <payload or null>, "error": <message or null>, "status": <HTTP code> }. This invariant holds for every HTTP status code from 200 to 502.Backend modules
The NestJS application is composed of eight focused modules. Each module encapsulates its own controllers, services, and repository interactions.AuthModule
Handles student registration (
POST /api/v1/auth/register) and login (POST /api/v1/auth/login). Signs JWTs with HS256 using JWT_SECRET (24-hour expiry). Passwords are hashed with bcrypt at 12 rounds. These two endpoints are explicitly excluded from the global JwtAuthGuard.TaskModule
Full CRUD for academic tasks via
GET, POST, PATCH, and DELETE /api/v1/tasks. All queries are scoped to the authenticated student’s id. Tasks are returned sorted by deadline ascending. Deletion sets is_deleted = true — the record and all associated micro-objectives are preserved for audit purposes.SessionModule
Manages the EMA session lifecycle: start (
POST /api/v1/sessions), submit fatigue score (POST /api/v1/sessions/:sessionId/fatigue), and retrieve history (GET /api/v1/sessions/history). Validates that Fatigue_Score is an integer in [1, 5] before persisting. Delegates to TaskDecomposerModule when score ≥ 4.TaskDecomposerModule
Calls an external LLM service over HTTP using
AI_SERVICE_API_KEY. Enforces the cardinality invariant (2–7 micro-objectives) and the duration invariant (each ≤ 25 minutes). Returns HTTP 502 with the original task as fallback if the AI service is unreachable.DashboardModule
Serves
GET /api/v1/dashboard — a single aggregated endpoint that returns active tasks (sorted by deadline), pending micro-objectives grouped by parent task, and the last 30 fatigue scores ordered chronologically. All data is strictly isolated to the authenticated student.NotificationModule
A cron-based worker that fires hourly. Finds tasks with pending micro-objectives due within 24 hours, suppresses dispatch if the student has an active session, enforces the 3-notifications-per-24h cap, and retries failed deliveries up to 3 times. Every dispatch attempt (success or failure) is logged to
notification_logs.DbWriterModule
The single repository layer for all database writes. Uses Prisma’s type-safe, parameterised query builder — no raw SQL concatenation anywhere in the codebase. Retries failed writes once after 500 ms. Operates with a connection pool of min=2 / max=10 connections.
SessionSerializerModule
Provides lossless JSON serialisation and deserialisation of
Session and FatigueRecord objects. Guarantees type preservation — Fatigue_Score must remain a JSON integer, never a string or float. Rejects payloads with missing required fields or type mismatches without constructing any partial object.API layer
The NestJSmain.ts bootstrap configures the following cross-cutting concerns that apply to every endpoint in the application:
| HTTP Status | Trigger |
|---|---|
201 Created | Successful resource creation (session, task, registration) |
200 OK | Successful read or update |
401 Unauthorized | Missing, invalid, or expired JWT |
403 Forbidden | Attempting to access another student’s resource |
404 Not Found | Undefined route or non-existent resource |
409 Conflict | Registration with a duplicate email address |
422 Unprocessable Entity | Payload fails ValidationPipe rules |
502 Bad Gateway | External LLM service unavailable during task decomposition |
Data layer
The Prisma schema defines six models mapping to six PostgreSQL tables. All primary keys are UUIDs generated server-side withgen_random_uuid(). All timestamps use TIMESTAMP WITH TIME ZONE (stored in UTC) to avoid timezone ambiguity in the fatigue history chart.
Entity overview
Model reference
| Model | Table | Key fields |
|---|---|---|
Student | students | id (UUID PK), email (UNIQUE), passwordHash, createdAt, updatedAt |
Session | sessions | id, studentId (FK), startedAt, endedAt (nullable), isActive |
FatigueRecord | fatigue_records | id, sessionId (FK), studentId (FK), fatigueScore (SmallInt, CHECK 1–5), recordedAtUtc |
Task | tasks | id, studentId (FK), name, description (nullable), deadline, isDeleted, createdAt, updatedAt |
MicroObjective | micro_objectives | id, taskId (FK), sessionId (FK), content, estimatedMinutes (SmallInt, CHECK 1–25), isCompleted, isAuditOnly |
NotificationLog | notification_logs | id, studentId (FK), taskId (FK), status (pending/sent/failed), attemptCount, dispatchedAtUtc |
is_deleted = true) to preserve the micro-objective audit trail.
Connection pool is configured in Prisma with min: 2 and max: 10 connections, balancing availability with resource consumption in the containerised environment.
PostgreSQL 18 (used in the Docker Compose setup) stores its data at
/var/lib/postgresql/18/docker inside the container. The compose.yml mounts the named volume mindflow_pgdata at /var/lib/postgresql — the parent directory — to ensure persistence across restarts and forward compatibility with future PostgreSQL major versions.Frontend architecture
The Next.js 14 frontend uses the App Router exclusively. Pages and layouts are co-located with their server and client components inapps/frontend/src/app/.
Auth.js handles the authentication session on the client side, using the AUTH_SECRET environment variable. It stores the JWT returned by the NestJS backend and attaches it as a Bearer token to all outbound API requests.
SWR is used for all data fetching on the client. It provides automatic revalidation and optimistic updates for dashboard interactions such as marking micro-objectives as complete — the UI reflects the change within 2 seconds without a full page reload.
Shadcn/ui + Tailwind CSS form the base design system. All structural UI components (Button, Input, Card, Dialog, Badge, Checkbox, Tabs, Skeleton) come from Shadcn/ui, which copies component source directly into the repository rather than wrapping an opaque third-party package.
Wrapper Pattern — any third-party animation or visual-enhancement library is isolated behind a thin React wrapper component located in apps/frontend/src/components/wrappers/. Wrappers are loaded with Next.js dynamic() and { ssr: false } so they never block server-side rendering. Each wrapper exposes a minimal, MindFlow-specific prop interface and provides a static Tailwind fallback if the library fails to load:
The Wrapper Pattern enforces a strict rule: no core component (
TaskList, EmaChat, FatigueHistoryChart) imports a third-party animation library directly. Only wrappers do. This guarantees that the full application compiles and remains functional even if every animation library is removed.