Demo School Seed
The API-driven seed that builds a populated demonstration school - what it creates, the environment it reads, the checks that refuse a run, and the five demo logins.
Audience: Backend developers - anyone who needs a populated development database Scope:
pnpm --filter @skoolsewa/api seed:demo-school; the bootstrap seed (pnpm db:seed) is covered only where the two meet
Demo School Seed
The demo seed creates a working school - staff, pupils, guardians, a published timetable, attendance,
exams, fees, salary and assets - by calling the running API as the superadmin. It writes through the
same controllers, validation, outbox and workers as the admin panel, so every side effect the
product has also runs here. It lives in apps/api/scripts/seed-demo-school/ and is not part of the
Nest application.
1. Two seeds, two jobs
| Command | Creates | Needs the API running |
|---|---|---|
pnpm db:seed | Permissions, roles, one superadmin, reference lists, geography lists and the school structure. Idempotent. | No - it writes to the database directly |
pnpm --filter @skoolsewa/api seed:demo-school | The demonstration school described below. Run once per database. | Yes |
pnpm db:seed runs first: the demo seed logs in as the superadmin it creates and relies on the
roles, grades, sections and the current academic session it leaves behind.
2. Running it
- Migrate and bootstrap the database:
pnpm db:migrate, thenpnpm db:seed. - Start the API with the full runtime profile (
API_RUNTIME_PROFILEunset orfull):pnpm dev:server. - Run the seed:
pnpm --filter @skoolsewa/api seed:demo-schoolThe full profile is required because several steps wait on queue workers that the fast profile does
not start: approved leave writing attendance, fee generation runs, and salary approval. Each of those
is polled to its terminal state; a step times out rather than assuming the worker ran.
The runner prints ▶ <step> and ✔ <step> <counts> <ms> lines, then a summary of every step's
counts. The first failing step prints one ✖ line (step name, HTTP status, error code, method, path
and message - never a request body or a password), sets exit code 1 and stops the run.
3. Environment
config.ts loads apps/api/.env without overriding variables already exported in the shell.
| Variable | Required | Default | Meaning |
|---|---|---|---|
SEED_API_BASE_URL | No | http://127.0.0.1:5002/api | API base URL, trailing slash removed. Must be https, or http on localhost, 127.0.0.0/8 or ::1, so the superadmin password never crosses a network in cleartext. |
SUPERADMIN_EMAIL | No | superadmin@skoolsewa.local | Account the seed signs in as. |
SUPERADMIN_PASSWORD | Yes | - | Password of that account. |
DEMO_PASSWORD | No | demo1234 | Password given to the five demo logins. 8 to 32 characters, the range the login DTO accepts. |
SEED_EXPECT_DB | Yes | - | Name of the database the seed must be connected to. Must not end in _test. |
DIRECT_DATABASE_URL, else DATABASE_URL | One of them | - | Direct PostgreSQL connection used for the run lock, the identity checks and writing the demo credentials. Its database name must not end in _test. |
A missing required variable, a _test database name (in SEED_EXPECT_DB or in the URL, including a
percent-encoded skoolsewa%5Ftest), an unparsable database URL, an unsafe SEED_API_BASE_URL or a
DEMO_PASSWORD outside 8 to 32 characters stops the run before any request is sent.
4. Preflight refusals
The preflight runs before the first write and refuses, with a message naming the condition:
| Check | Refusal |
|---|---|
| API health | GET /health/ready (outside the /api prefix) gives no answer within 10 seconds, or answers a non-2xx status. |
| Same database | The direct connection reports a database other than SEED_EXPECT_DB, or the superadmin's user id seen through the API differs from the one in the direct connection - the API and the seed point at different databases. |
| Empty roster | GET /students or GET /guardians (removed records included) reports any rows. Reset the database first. |
| Demo logins free | Any of the five demo emails already exists. |
| Current session | There is not exactly one current academic session, or it does not run 2026-04-15 to 2027-04-14 - the only session the BS 2083 data fits. |
| Run window | The date in Asia/Kathmandu is outside 2026-09-17 to 2027-02-17. The floor guarantees Bhadra 2083 has ended (salary periods need it) and the First Terminal is in the past; the ceiling leaves at least eight weeks before the session ends. |
| Run lock | Another seed run holds the PostgreSQL advisory lock. |
When NODE_ENV is production, the preflight also prints whether RESEND_API_KEY and
SMS_PROVIDER are set (never their values), because a run can send mail and SMS.
5. What it creates
The steps run in this order. Names and numbers come from the step files.
| Step | Creates |
|---|---|
00-fee-sweep-off | Switches fee auto-generation off for the run and records the original value. |
01-school-profile | Names the school "Shree Saraswati Demo Secondary School", timezone Asia/Kathmandu, currency NPR. |
02-week-config | Saturday as the weekend; the school day ends at 17:00. |
03-calendar | Generates the current session's calendar and adds four holidays: Buddha Jayanti, Haritalika Teej, the Dashain vacation (2026-10-11 to 2026-10-25) and the Tihar vacation (2026-11-07 to 2026-11-11). |
04-departments-designations | Ten teaching departments beside the existing Administration department, and their designations. |
05-rooms | 33 rooms: 30 classrooms, two laboratories and the library. |
06-subjects | 17 subjects. |
07-staff | 47 staff: 39 teachers across the teaching departments, plus the principal, vice principal, three office assistants, an accountant, a librarian and a peon. |
08-classes | 30 classes: Nursery to Grade 12, sections A and B. |
09-guardians-students | 150 families: 200 guardians and 180 students, each student enrolled in a class. |
10-class-subjects | Subject and teacher assignments for every class. |
11-timetable | A published weekly timetable for every class, Sunday to Friday. |
12-logins | Grants sign-in to the five demo accounts and gives them credentials (section 6). |
13-leave | Two pupil leave requests (one approved, one pending, the pending one on future days) and one approved staff leave. The leave worker writes the approved days into attendance. |
14-attendance | A completed register for every class on every school day from the later of the session start and 28 days ago, to yesterday. Marks are drawn 92 percent present, 4 absent, 3 late, 1 excused; days the leave worker already excused are kept. |
15-exams | Two exams for all 30 classes (section 7). |
16-assignments | Three published assignments per class (90), and one reviewed submission each for the student and student2 logins. |
17-notices | Ten published notices, addressed to everyone, to a grade, to a class (or its guardians or teachers) or to a role. |
18-fees | One fee category, three components (tuition monthly, admission one-time, examination annual), one fee structure per grade band, a generation run per grade, and payments that leave pupils fully paid, partly paid and unpaid. |
19-salary-assets | One salary structure assigned to every staff member; Shrawan and Bhadra 2083 prepared, approved and sent; 12 assets (6 laptops, 6 desks) assigned to staff. |
20-finish | Restores fee auto-generation and prints the login emails. |
Randomised values (names, attendance marks, payment mix) come from a fixed-seed generator, so two runs on the same date produce the same data.
Fee auto-generation recovery
Step 00 turns the fee auto-generation sweep off so it does not race the seed's own generation runs,
and step 20 turns it back on. If a step fails in between, the runner prints a recovery line after
the failure:
recovery: fee auto_generate is now false; restore it to true (PATCH /fees/settings {"autoGenerate": true}), or reset the databaseThe value in the line is whatever the setting was before the run. Restore it through the API, or reset the database and run again.
6. The five demo logins
| Key | Name | Role | |
|---|---|---|---|
office | Anita Shrestha | Staff (office) | office@skoolsewa.local |
teacher | Bikash Adhikari | Staff and teacher | teacher@skoolsewa.local |
parent | Chandra Rai | Guardian | parent@skoolsewa.local |
student | Deepa Rai | Student | student@skoolsewa.local |
student2 | Aarav Rai | Student | student2@skoolsewa.local |
All five share the password in DEMO_PASSWORD. The seed grants each account sign-in with no
invitation sent, then writes the email and password credential straight to the database in one
transaction (bcrypt, cost 12, email marked verified) - all five get a credential or none does. It
then signs in as each account and checks the user id matches the one the API created; the teacher
also switches to the teacher role. The activity steps reuse these sessions: the leave requests are filed as the
parent and teacher logins and the assignment submissions as the two student logins; everything
else is written as the superadmin.
parent, student and student2 belong to one family, the Rai family.
7. Exam dates
| Exam | Dates | State at the end of the run |
|---|---|---|
| First Terminal Examination 2083 | School days from 2026-08-02 to 2026-08-31 (fixed) | Papers published, marks entered and finalised for every paper, exam completed, results computed for all 180 pupils and published. |
| Second Terminal Examination 2083 | Starts 14 days after the run date and spans 33 days (secondTerminalRange in 15-exams.ts); papers sit on school days in that range | Papers published, no marks. |
The step refuses to continue unless the last First Terminal paper falls before today and the first Second Terminal paper falls after it. Example: a run on 2026-10-08 places the Second Terminal window from 2026-10-22 to 2026-11-23.
8. Running again
The seed is not idempotent. A second run stops at the preflight because the roster and the demo
logins already exist. Reset the database (drop, pnpm db:migrate, pnpm db:seed) to start over.
A run that fails part-way leaves its rows in place; the roster check then blocks a rerun, so reset the database as well.
Down Migrations
The down-migration convention, the pnpm db:migrate:down runner, which migrations can be reverted, and how to prove a down file is correct.
Storefront Integration
How skoolsewa-frontend consumes the API — CMS pages, cache tags, banners, analytics and account deletion — and the contracts that must not drift.