Overview
I built the full platform behind Visual Studios Lens, a South Asian and Muslim wedding photography studio serving NY, NJ and CT. It is a public website plus Studio OS, the internal system the studio runs on, covering quotes, bookings, contracts, invoices, galleries and email.
The frontend is Next.js 16 and the API is NestJS 11 on PostgreSQL, with real-time updates over Socket.IO.
Problem
A wedding studio runs on scattered tools: a brochure site, a separate gallery host, an email inbox, spreadsheets for money, and chat threads for the team. Clients get messages from five places, and the studio cannot see one client's whole story in one screen.
Approach
I built one platform with two faces. The public site is fast and SEO-ready, and Studio OS gives the team ten connected apps over a single API. Clients never need an account: every quote, contract, invoice and gallery opens from a private link. Real-time updates and email run on the same backend, so nothing lives in a separate tool.
Outcome
- Frontend routes
- 112
- API endpoints
- 281
- Database tables
- 43
- Studio OS apps
- 10
- Merged pull requests
- 296
- Lines of TypeScript
- ~98k
What I built
- Public website with a cinematic portfolio, love stories, journal, prices, packages, store and video pages, all editable from the admin
- SEO built in: structured data, sitemap, per-page metadata and fallback content so pages never render empty
- Studio OS: ten internal apps (website admin, operations, team hub, studio manager, client gallery, job history, company brain, brand kit, nightly review, company vault)
- Quotes and inquiries that flow into a CRM with contacts, projects, bookings and a shoot-day planner
- Contracts with e-signatures, invoices with derived status, and recorded payments
- Client galleries with WebP previews, PIN or password protection, quick share links and gallery reminders
- Client journey page that gathers quotes, contracts, invoices, shoots and galleries behind one private link
- Studio Inbox: real email in and out through Amazon SES, threaded, with attachments and live updates
- Team Hub: chat, per-job threads, tasks and notifications with server-scoped access
- Live visitor traffic for admins over Socket.IO
- Pixieset import for moving existing galleries across
- JWT sign-in with rotating refresh tokens, admin and member roles, throttling and request validation
- Prometheus metrics, Docker multi-stage images and a CI pipeline on both repositories
Architecture
System architecture
Studio OS: ten apps, one sign-in
Data model
Client lifecycle
Gallery delivery
Studio Inbox
Real-time channels
Security layers
Delivery pipeline
Engineering decisions
01Client pages use random links, not accounts
Clients open galleries, contracts, invoices and journey pages from links made of 18 random bytes. There is nothing to sign up for or forget, and the links cannot be guessed. Galleries add a bcrypt-checked PIN or password on top.
02Originals stay in Google Drive
Full-size files stay in the studio's own Drive, so storage costs stay low and nothing is copied twice. The platform makes small WebP previews with sharp and fetches the original only when a client downloads.
03Money is derived, not stored
An invoice's status (draft, sent, partial, paid, past due) is worked out from its payments each time it is read. A stored status could drift from the payments, and the totals the studio sees would be wrong.
04Webhooks trust signatures, not URLs
The email webhooks are public by necessity. Each request is accepted only after its SNS signature verifies and its topic matches the expected one exactly, so a guessed URL does nothing.
05Real-time is scoped on the server
Socket.IO connections need a valid token, and rooms are filled by the server. A team member never receives a job thread or website task they could not open through the normal app.
06A test that guards the entity list
The API lists its database entities explicitly. A missing one once caused a production error on a single endpoint, so a spec now fails if any entity class is left out of that list.
07Small pull requests with a fixed gate
The work shipped as 296 merged pull requests across both repositories. Lint, format, tests, build and a malware scan run on every one, which kept a fast-moving product stable.
Need something similar?
Tell me what you are building and I will reply within one working day.



