sakibkx
Start a project

Delegate operations for an international conference

Hundreds of university representatives and government officials fly into China for one conference. Every one of them needs a formal invitation letter to get a visa. If the letter is late or wrong, they cannot enter the country.

RoleBuilt the backend end to end. Led the frontend.
Authored by me94% of the backend
Year2026
StatusLive
Visit brcu.eduapplify.com
Measured
API endpoints281@Get/@Post/@Patch/@Put/@Delete decorators counted across 24 controllers
Data model50 tablesmodel declarations in a 1,916-line Prisma schema, plus 26 enums
Migrations38migration directories, 3,082 lines of hand-written SQL, applied to a live database
Doc coverage83%234 ApiOperation annotations against 281 endpoints
TODO markers0grep for TODO/FIXME/HACK across 30,039 lines, cross-checked against all 130 commits

How authorship was measured. Lines surviving in the current tree, measured with per-file git blame across all tracked source, excluding lockfiles and generated code. 95.4% of commits are mine.

The problem

An international education conference in China brings in hundreds of university representatives, government officials and education partners from dozens of countries. Every one of them needs a formal invitation letter before they can apply for a Chinese visa. A letter that arrives late, or with a misspelled passport name, means that person does not get on the plane.

Once they land, five different desks touch the same person: invitation, travel, hotel, venue and programme. All five update the same record at the same time, and the organiser needs to know where any delegate is at any moment.

It was being run on spreadsheets.

What I built

I designed and built the backend end to end, and led the frontend where I own the data layer, the permission engine, the admin portal and the QR subsystem.

The invitation letter is generated on demand and never written to disk. An admin edits a Markdown template per programme. The renderer distinguishes three kinds of token, so a value can be injected safely inside an HTML attribute as well as in a table body. It renders to A4 through headless Chromium, waits for document.fonts.ready so the web fonts actually apply, and embeds a per-guest QR code as a data URI. One browser instance is kept alive for the process lifetime. If Chromium crashes, the cached promise is dropped so the next render self-heals, and a failed launch clears the cache too, because otherwise every later render reuses the rejected promise and PDF generation stays broken until someone restarts the process.

Access control is two orthogonal layers. One controls which module a staff member works in. The other controls which of nine QR status transitions they may perform. Most systems collapse these into a single role field and then cannot express the thing the client actually needed: a hotel staffer who may scan check-ins but not checkouts. The nine flags are stored as nine columns and mapped to a nested object in exactly one file.

Authentication has a real revocation path. Refresh tokens are opaque random strings stored only as hashes, never as tokens that can be read. Replaying an already-rotated token revokes every outstanding token for that user. Logout verifies the signature before blacklisting, so a forged token cannot be used to flood the blacklist table.

Passport data is read by Gemini against a generated JSON schema, with five distinct failure boundaries that all degrade to asking the person to type it in.

The part that was hardest

Thirty-eight migrations applied to a live database with no shadow copy. Converting a free-text country field into a 250-value enum through a CASE alias map. Collapsing four separate status enums, which meant dropping and recreating column defaults around each type swap. Unpacking a Postgres enum array into boolean columns.

Human-readable delegate codes had to be race-free, so the sequence reset and the next value are taken in one SQL statement. A seeded database cannot hand back a code that is already in use.

Stack
  • NestJS 11
  • TypeScript
  • PostgreSQL 16
  • Prisma 7
  • Next.js 16
  • Puppeteer
  • Google Gemini
Scope of the claim

What this project does not prove

  • This system has no published usage figures. I can tell you how large it is, not how many people used it.
  • It has no automated tests. I verified it in a browser and I would not do that again.
  • All delegate documents sit on one server's disk. That was a deliberate trade-off for the deadline, not an architecture for scale.