05 / 05 · NUS Orbital 2024
GeNiUS
Search first, ask second, and find a mentor if you’re still stuck.
TypeScript · Next.js 14 · Auth.js · Zod · TanStack Query · PostgreSQL · Drizzle · Docker · Vitest · Playwright

The problem
NUS students often don’t know who to ask for module help, hesitate to ask in public, or want advice from someone who already took the module. Ed Discussion gets little input from seniors, and Telegram chats offer no anonymity and no way to check who’s answering. GeNiUS adds module-tagged posts, anonymous posting, and profiles that show the modules each person has taken.
My role
Team of two for NUS Orbital 2024. I built discussions and comments, their pagination, admin moderation, and a large share of the tests.
How it works
- InputPost formTitle, body, tag, up to 3 images
- Auth gateCheck session userCaller must be the author
- Schema gateValidate and filterZod schema and profanity list
- MediaConvert and hash imagesAVIF files named by SHA-1
- StoreWrite in transactionRow and counters change together
- OutputRank and loadCursor pages, most upvoted first
Model stepHuman or code gatePlain step
What I built
- 01Built discussions and comments: create, edit, delete, resolve, anonymous posts, nested replies and upvotes
- 02Added progressive comment loading with cursor pagination ordered by upvotes, using TanStack Query so new comments appear at once
- 03Wrote tests across the suite (122 end-to-end and about 79 unit tests between the two of us), including failure cases for the database, image processing and email
- 04Added admin moderation, and automatic tag creation from the NUSMods module list
Trust boundaries · who can do what
- APasswords are stored as salted scrypt hashes; verification emails are limited to one per address every 5 minutes.
- BA profanity filter checks titles and bodies, and admins can delete content but never edit it.
- CAnonymous names are masked on the server, and deleted posts keep their IDs so replies keep their context.
Hardest part
Comments are ranked by upvotes, which change while people scroll, so page numbers would skip or repeat. I used a cursor that reads the last comment’s upvotes and date and fetches only what ranks after it. Deleted comments stay for their replies and are placed after the live ones.
Outcome
Was deployed as a live site and presented at Orbital 2024 across three milestones. CI runs lint, unit tests and Playwright end-to-end tests on every push and pull request.
Screens



