case study · 2026

MenuQR: PDF menus to QR codes, shipped through a tested CI/CD pipeline

Turns a restaurant's PDF menu into a mobile-friendly menu page and a printable QR code. A full-stack Next.js app where every change is type-checked, built and tested end to end before Render deploys it.

role
Solo project
timeline
2026
stack
Next.js 16, React 19, TypeScript, MongoDB, Cloudflare R2, GitHub Actions
status
Live demo
Screenshot of MenuQR
01

What it does

Restaurants sign up, upload their menu and put the QR code on their tables. Guests scan it and the menu opens on their phone, with no app and no PDF viewer. The project started as the one-endpoint starter app from a CI/CD crash course; everything after that first commit is my own work.

  • Upload a menu PDF (up to 25 MB) and get a readable link such as /m/escape-cafe, plus an SVG QR code that prints sharply at any size.
  • Replace the PDF at any time: the link and QR code never change, so printed codes keep working.
  • Manage several menus (lunch, dinner, drinks) from one dashboard.
  • PDF.js draws each page at the phone's width and pixel density; the first page appears while the rest load, and the menu is cached until it changes.
02

CI/CD pipeline

  • Every pull request and push to main runs a GitHub Actions job: clean install, strict type-check, production build, then the full test suite against a real MongoDB service container.
  • CI runs the same npm run verify command developers run locally, with a read-only token and timeouts so a hung server fails in seconds.
  • Render deployment is defined as code and only releases commits whose checks pass. Traffic switches to a new version only after its health check returns 200.
  • /api/version reports the deployed commit, and the guardrails were proven by opening a pull request with a deliberately failing test.
03

Testing

  • 16 business-logic tests cover accounts, sessions, upload validation and ownership rules, including that one user can never change another's menu.
  • 13 end-to-end tests start the real production build and submit the actual Server Action forms: sign up, upload, view, replace and delete.
  • No database mocks: each test file creates and drops its own throwaway MongoDB database, so tests can never touch real data.
04

Security

  • Passwords hashed with scrypt and a per-user salt; session tokens are random, stored only as hashes, and expire automatically.
  • Every replace or delete looks a menu up by ID and owner.
  • Uploads are checked by the file's contents (the PDF signature), not its name or type.
05

Architecture

Business rules live in plain TypeScript with no Next.js imports; pages and Server Actions are thin wrappers around them. Storage is pluggable: Cloudflare R2 in production, a local folder in development and memory in tests.

  1. Pages & Server Actions
  2. Business logic (lib/)
  3. MongoDB
  4. Cloudflare R2
06

Results

29
unit & end-to-end tests on every change
next projectGameZone