Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/vruizz22/innova-backend-serverless/llms.txt

Use this file to discover all available pages before exploring further.

By the end of this guide you will have the Innova API running on http://localhost:3000, a valid JWT in hand, and a successful student attempt response from the rule engine — all without touching real AWS infrastructure. Local SQS and S3 are emulated by LocalStack inside Docker, so every code path (including the telemetry FIFO queue and the LLM fallback queue) works on your laptop.

Setup

1

Prerequisites

Make sure the following are installed before you begin:
ToolMinimum versionNotes
Node.js≥20Use nvm or fnm to manage versions
pnpm≥9corepack enable && corepack prepare pnpm@latest --activate
Docker + Docker Composev2Required for MongoDB and LocalStack
Supabase projectNeeded for Auth (JWKS endpoint) and Postgres
You can create a free Supabase project at supabase.com. Copy the Project URL, anon key, and service role key from the project dashboard — you will need them in the next step.
2

Install dependencies

Clone the repository and install Node packages with pnpm:
git clone https://github.com/vruizz22/innova-backend-serverless.git
cd innova-backend-serverless
pnpm install
3

Configure environment

Copy the example environment file and fill in the required values:
cp .env.example .env
Open .env and set at minimum the six required variables below. Leave optional variables (AI provider keys, Resend) blank for local development.
VariableDescriptionExample value
DATABASE_URLSupabase Postgres connection string (local dev uses :5433)postgresql://postgres:innova_secret@localhost:5433/innova_dev_db?schema=public
MONGODB_URIMongoDB Atlas or local telemetry connectionmongodb://root:innova_mongo_secret@localhost:27017/innova_telemetry_local?authSource=admin
SUPABASE_URLYour Supabase project URLhttps://<project-ref>.supabase.co
SUPABASE_SERVICE_ROLE_KEYServer-only admin key (bypasses RLS)From Supabase dashboard → Settings → API
AWS_REGIONAWS region for SQS/S3 resourcesus-east-1
CORS_ORIGINSComma-separated allowed browser origins — required at boothttp://localhost:3001,http://localhost:3002
The remaining SQS_* URLs in .env.example are pre-filled with LocalStack-compatible placeholders. Swap in real ARNs only when deploying to AWS.
4

Start local infra

Bring up MongoDB 7 and LocalStack (SQS + S3) in the background:
docker compose up -d
Verify the containers are healthy:
docker compose ps
You should see both mongodb and localstack in a running state. Postgres comes from Supabase (or a local instance pointed to by DATABASE_URL) — it is not managed by Docker Compose in this project.
5

Apply migrations and seed

Run the Prisma migration to create all tables in Postgres, then seed the database with a demo school, courses, students, and exercises:
pnpm prisma migrate dev
pnpm prisma db seed
To also import the full innova-ai-engine error taxonomy into the ErrorTag table (required for the admin catalog and LLM classifier), run the full seed script instead of the plain seed:
pnpm seed:full
pnpm seed:full already runs prisma db seed internally — you do not need to run both.
6

Start the dev server

Launch the NestJS dev server with hot reload:
pnpm start:dev
NestJS will print its bootstrap log to the terminal. Wait for the Nest application successfully started message — the server is now listening on http://localhost:3000.
7

Verify the server is up

In a separate terminal, confirm the API is responding:
curl http://localhost:3000/
Expected response:
Innova Backend Serverless is running!
A 200 OK with this message means the NestJS app bootstrapped successfully and all module connections (Prisma, Mongoose) initialised.
8

Browse Swagger UI

Open http://localhost:3000/docs in your browser to explore the full interactive OpenAPI specification. Every endpoint is documented with request/response schemas generated directly from the TypeScript DTOs.The raw OpenAPI JSON is available at http://localhost:3000/docs/openapi.json.

Your first API call

In local development, the API uses a local HS256 JWT (issued by POST /auth/login) rather than a Supabase JWKS RS256 token. Both token types are validated by the same SupabaseAuthGuard — the guard falls through to the local strategy when no Supabase JWKS is reachable. In production, all tokens are RS256 JWTs issued by Supabase Auth.

1. Obtain a JWT

Log in with a demo teacher account to receive an access token. If you have not created demo users yet, see the tip below about pnpm seed:auth.
curl -X POST http://localhost:3000/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"teacher@innova.demo","password":"Innova123!"}'
A successful response returns an HS256-signed accessToken plus a refreshToken and the user profile:
{
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "tokenType": "Bearer",
    "expiresInSeconds": 3600,
    "user": {
      "id": "<user-uuid>",
      "email": "teacher@innova.demo",
      "role": "TEACHER",
      "profileId": "<teacher-uuid>",
      "supabaseUid": null,
      "tokenVersion": 0
    }
  }
}
Copy the value of accessToken — you will pass it as a Bearer token in every subsequent request.

2. Submit a student attempt

Send a POST /attempts request with the student’s rawSteps array, topicCode, expectedAnswer, and studentAnswer. The rule engine classifies the procedural error synchronously and returns the errorTag and updated pKnown mastery value in the response.
curl -X POST http://localhost:3000/attempts \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_JWT>' \
  -d '{
    "studentId": "<student-uuid>",
    "topicCode": "T-SUB-BORROW",
    "exerciseId": "<exercise-uuid>",
    "rawSteps": [
      { "expression": "53 - 26 = 33", "isFinal": false },
      { "expression": "33", "isFinal": true }
    ],
    "expectedAnswer": 27,
    "studentAnswer": 33
  }'
A classified response looks like:
{
  "data": {
    "attemptId": "clx...",
    "isCorrect": false,
    "errorTag": "SUBTRACTION_BORROW_OMISSION",
    "confidence": 0.92,
    "source": "rule",
    "pKnown": 0.28
  }
}
If the rule engine could not classify the error, errorTag will be "UNCLASSIFIED" and the attempt is queued for async LLM classification via SQS. Poll GET /attempts/:id/status for the resolved classification.

Useful development commands

CommandWhat it does
pnpm start:devStarts the NestJS server with hot reload on port 3000
pnpm buildCompiles TypeScript with nest build (output in dist/)
pnpm testRuns the full Jest unit test suite
pnpm test:covRuns tests with coverage report (gate ≥75%)
pnpm prisma studioOpens Prisma Studio — a browser GUI for the Postgres database
docker compose logs -fTails live logs from MongoDB and LocalStack containers
Seeding demo auth users: run pnpm seed:auth to create the demo teacher, student, and parent identities in Supabase Auth via the Admin REST API. This script is idempotent and guarded by two environment variables — you must set ALLOW_SEED=1 and SEED_DEMO_PASSWORD=<your-chosen-password> before running it. This keeps the local Postgres seed data and Supabase Auth in sync so JWTs issued by Supabase match the User rows in your database.
LocalStack and offline AWS flows: the docker-compose.yml provides a local LocalStack instance that emulates SQS and S3. This means you can exercise the full attempt telemetry FIFO queue, the LLM classification queue, and the OCR queue without an AWS account or real SQS URLs. To drain the attempt-reprocess queue locally (the OCR→attempt loop), run pnpm consume:reprocess in a separate terminal alongside pnpm start:dev.

Build docs developers (and LLMs) love