Skip to content

Getting Started

Audience: Developers joining the SIS backend team. Goal: Understand the project, set up locally, and start coding.


1. Prerequisites

Before diving into the codebase, make sure you're comfortable with the foundational topics below. You don't need to be an expert — a working understanding is enough. Each subsection explains why the topic matters here.

How the Web Works

Every feature in this project starts with an HTTP request and ends with an HTTP response. Understanding the client-server model, URLs, HTTP methods, status codes, headers, and the request/response cycle is essential for reasoning about anything the backend does.

Terminal / Command Line

The dev server, database migrations, tests, linting, and git — all run from the terminal. You'll spend significant time there, so fluency with basic shell commands, navigating directories, and reading command output is non-negotiable.

Git & Version Control

All code changes go through Git. We use feature branches, pull requests, and code review. You need to understand commits, branches, merges, diffs, and how to resolve conflicts.

Programming Fundamentals (OOP)

NestJS is built on classes, interfaces, inheritance, and dependency injection. If you don't have a solid grasp of object-oriented programming concepts — classes, constructors, access modifiers, abstract classes, interfaces — the framework code will be hard to follow.

TypeScript / JavaScript

The entire backend is TypeScript. You need to be comfortable with modern JavaScript (async/await, destructuring, arrow functions, Promises) and TypeScript's type system (interfaces, generics, type narrowing, union types).

SQL & Relational Databases

All data lives in PostgreSQL. Even though Prisma abstracts most queries, you need a mental model for tables, rows, columns, primary keys, foreign keys, JOINs, and indexes to understand what Prisma generates and to debug data issues.

Package Managers (npm)

npm install, npm run, and npx are used constantly. Understanding how package.json, node_modules, lock files, and scripts work will save you from common "it doesn't work on my machine" issues.

REST APIs

This project exposes a REST API. You need to understand REST conventions: resources, HTTP methods (GET, POST, PATCH, DELETE), status codes (200, 201, 400, 401, 404), request/response bodies in JSON, and how clients interact with APIs.

What a Web Framework Is

Express.js runs underneath NestJS. Understanding why web frameworks exist — routing, middleware, request parsing, error handling — helps you appreciate what NestJS adds on top (modules, DI, decorators, guards).

Backend Design Patterns

This codebase uses Controller-Service architecture, dependency injection, and decorator-based metadata extensively. Knowing these patterns (and why they exist) makes the code structure intuitive rather than mysterious.

Authentication & Authorization

The project implements JWT-based authentication and role-based authorization. Understanding the difference between authentication ("who are you?") and authorization ("what can you do?"), how tokens work, and why passwords are hashed will make the auth and permissions modules much easier to follow.


2. Tech Stack

Layer Choice
Runtime Node.js 20+, TypeScript (ES2023 target, nodenext modules)
Framework NestJS 11
ORM Prisma 7.3 with PrismaPg adapter
Database PostgreSQL (UUID PKs, JSONB for custom fields)
Auth Passport.js + JWT (argon2 passwords)
Validation class-validator + class-transformer
API docs Swagger via @nestjs/swagger
Testing Jest 30 + ts-jest (ESM mode) + Supertest
Linting ESLint flat config (type-checked) + Prettier
Deployment Railway (BE) + Cloudflare Workers (FE)

How a request flows through the stack

┌──────────────────────────────────────────────────────────────────┐
│  A request arrives at POST /api/v1/students                      │
│                                                                  │
│  1. Express        receives the HTTP request                     │
│  2. NestJS         routes it to the right controller method      │
│  3. Passport.js    validates the JWT token (is this user real?)  │
│  4. ScopeGuard     checks permissions (can this user do this?)   │
│  5. class-validator validates the request body (is the data ok?) │
│  6. Service        runs business logic                           │
│  7. Prisma         talks to PostgreSQL (reads/writes data)       │
│  8. Interceptor    filters response fields (hide sensitive data) │
│  9. Express        sends the JSON response back                  │
└──────────────────────────────────────────────────────────────────┘
Technology Role Think of it as...
NestJS Web framework The skeleton of the app. Organizes code into modules, handles routing, dependency injection.
TypeScript Language JavaScript with types. Catches bugs at compile time.
Prisma ORM Translates between TypeScript objects and SQL queries. You write prisma.student.findMany(), it runs SELECT * FROM students.
PostgreSQL Database Where all data lives. Tables, rows, relationships.
Passport.js Authentication library Handles "who are you?" — validates passwords and JWT tokens.
class-validator Validation library Handles "is this input valid?" — checks DTOs with decorators like @IsEmail().
Jest Test framework Runs your unit and e2e tests.
argon2 Password hashing Securely hashes passwords. Never store plain text passwords.

3. Project Structure

Project follows standard NestJS structure.

src/
├── main.ts                        # Bootstrap, global pipes/filters, Swagger
├── app.module.ts                  # Root module — only imports, no logic
└── generated/prisma/              # Auto-generated — never edit
├── prisma/                        # PrismaModule + PrismaService wrapper
├── common/                        # Shared: interfaces, filters, exceptions, decorators, DTOs, services
├── auth/                          # JWT + Passport (login/refresh/logout/me), cookie delivery
├── permissions/                   # Entity-Scope model (guard + interceptor + decorator + controller)
├── logger/                        # Pino structured logging (nestjs-pino + CLS context)
├── health/                        # GET /api/v1/health (public — no auth)
├── students/                      # Student CRUD (anagraphic + sensitive scopes)
├── teachers/                      # Teacher CRUD (5 scopes) + import
├── staff/                         # Staff CRUD (5 scopes) + import
├── users/                         # User read-only (profile + admin scopes)
├── departments/                   # Department + Grade CRUD (configuration scope, nested grades sub-resource)
├── rooms/                         # Room CRUD (configuration scope, canteen lunch shifts)
├── custom-fields/                 # Custom field definitions CRUD + validation (service-level permission checks)
├── curriculum/                    # Curricula + study plans CRUD (also used by setup wizard)
├── setup/                         # Tenant setup wizard (state machine, multi-step config)
├── ...                             # future domain modules

Each domain module encapsulates its controller, service, DTOs, interfaces, and tests. See docs/REFERENCE.md for module patterns and docs/architecture.md for full system design.


4. Local Setup

Setup

cp .env.example .env           # DATABASE_URL already points at Docker Compose Postgres
docker compose up -d           # Start local Postgres
npx prisma migrate dev         # Apply all migrations + generate client
npm run start:dev              # Start NestJS with hot reload

What's Running

  • Postgres 17 via Docker Compose on localhost:5432
  • NestJS natively on localhost:8080 (not containerized — avoids slow volume mounts on Windows, preserves hot reload)

Convenience Scripts

Script What it does
npm run docker:up Start Postgres container
npm run docker:down Stop Postgres container
npm run docker:reset Destroy volume + restart (wipes all data)
npm run db:setup Start Postgres + run migrations

Database Reset

To start fresh:

npm run docker:reset && npx prisma migrate dev

This destroys the Docker volume (all data), recreates the container, and re-applies all migrations.


5. Commands

npm run start:dev          # Dev server with hot reload
npm run build              # Prisma generate + NestJS build
npm run lint               # ESLint with auto-fix
npm run format             # Prettier
npm test                   # Unit tests (Jest)
npm run test:e2e           # E2E tests (Supertest)
npm run test:cov           # Coverage report
npx prisma generate        # Regenerate client after schema changes
npx prisma migrate dev     # Create + apply migrations
npx prisma db seed         # Seed data (tenant, roles, users, permissions)
npx prisma studio          # GUI for DB inspection
npm run docker:up          # Start PostgreSQL via Docker
npm run docker:reset       # Nuke volumes + restart with migration applied

6. Prisma Basics

  • Schema: prisma/schema.prisma — config: prisma.config.ts
  • Generated client: src/generated/prisma/ — never edit this directory
  • Import types: always use import type to avoid duplicating Prisma model types:
import type { User } from '../generated/prisma/client'
  • After schema changes: run npx prisma generate to update the client, then npx prisma migrate dev to create and apply the migration
  • Raw queries: use prisma.$queryRaw with tagged template literals to prevent SQL injection:
await this.prisma.$queryRaw`SELECT * FROM tenants WHERE id = ${tenantId}`

Full Prisma migration workflow (dev vs. deploy, migration immutability, Railway pipeline): chapter 10