Fig. 07 · Personal project

Prelude

A multi-tenant wedding-planning SaaS where every wedding is isolated and guests RSVP and share photos without an account.

Project
Prelude
Role
Designer and developer
Period
09/2026 - present
Context
Personal project
Stack
Next.js · TypeScript · MongoDB · Zod · Cloudflare R2 · Resend · Vercel
Outcomes
  1. Wedding-level multi-tenancy enforced in every backend query
  2. Account-free guest RSVP and photo sharing through hashed, unguessable tokens
  3. Bulk invitations sent in claimed batches without Redis or a worker fleet

Problem

Planning an Indian wedding means coordinating several events, hundreds of guests, vendors, tasks and expenses across a whole family - usually in spreadsheets and chat groups. Prelude gives the couple and their family one shared workspace, and gives guests a simple way to RSVP and share photos without signing up for anything.

Constraints

  • Private family data. One wedding must never see another’s guests, events or photos.
  • Guests will not create accounts. RSVP and photo sharing have to work from a link or a QR code.
  • Up to about 1,000 guests and 5,000 photos per wedding without changing the architecture.
  • Low operational overhead. A small team should be able to run it without managing servers, queues or caches.

Architecture

The figure follows a member’s request, a guest’s RSVP, a photo upload and a bulk invitation through the system.

  1. 01

    The wedding is the tenant

    Every private query is scoped by a wedding id derived from the signed-in member's membership - never from an id the browser sends - so one wedding can never read another's events, guests or photos.

  2. 02

    Guests never need an account

    Each guest gets an unguessable invitation token, stored only as a hash, to RSVP for the events they are invited to. A separate wedding-level token opens the shared gallery and its QR code.

  3. 03

    Photos skip the server

    The app validates type, size and access, then hands back a short-lived signed URL, and the phone uploads straight to R2. Large files never pass through the application server.

  4. 04

    Seven hundred invitations without a queue cluster

    Bulk sends become email jobs in MongoDB. A cron-triggered endpoint atomically claims small batches, sends them through Resend and retries a bounded number of times, so overlapping runs never send twice.

  5. 05

    Vendors found nearby, then owned

    Vendor search near the wedding's stored coordinates goes through the backend to Google Places; a saved vendor becomes the wedding's own record and no longer depends on the external service.

Fig. 07 - PreludeScale model
Couple&familyGuestsNext.jsmodularmonolithMongoDBAtlasCloudflareR2Emailjobs(cron)Resend(email)GooglePlaces
Fig. 07 · Plan - components, connections and data flows
Read the diagram as text

Components

Couple & family
Wedding members - admins and managers - who sign in to plan events, guests, tasks, vendors and expenses.
Guests
Invited guests who RSVP and share photos without ever creating an account.
Next.js modular monolith
One deployable app with domain modules - thin REST route handlers, Zod validation, business services and persistence.
MongoDB Atlas
Separate collections per entity, every private record carrying its wedding id; tokens and sessions stored as hashes.
Cloudflare R2
Wedding photos uploaded straight from the browser through short-lived signed URLs; only metadata lives in the database.
Email jobs (cron)
Bulk invitations queued in MongoDB and sent in small batches by a cron-triggered, secret-protected endpoint.
Resend (email) (external, not owned)
Transactional email provider behind an internal email service, so it can be replaced without touching business code.
Google Places (external, not owned)
Vendor discovery near the wedding location, called only from the backend; chosen vendors become the wedding's own records.

Flows

  • A member's request stays in their wedding: Couple & family → Next.js modular monolith → MongoDB Atlas.
  • A guest RSVPs without an account: Guests → Next.js modular monolith → MongoDB Atlas.
  • The app grants a short-lived upload: Next.js modular monolith → Cloudflare R2.
  • A photo goes straight to storage: Guests → Cloudflare R2.
  • Hundreds of invitations sent in batches: Next.js modular monolith → Email jobs (cron) → Resend (email).
  • Vendors discovered near the wedding: Next.js modular monolith → Google Places.

Key decisions & trade-offs

A modular monolith instead of services

The product is one Next.js application split into domain modules - weddings, members, events, guests, invitations, vendors, expenses, gallery and email - each with thin REST route handlers, Zod validation and its own service layer. Microservices would have added deployment and consistency problems the product does not have; module boundaries keep the option to split later.

The wedding is the tenant, derived on the server

Every private query is scoped by a wedding id resolved from the signed-in member’s membership, and object lookups check both the record id and the wedding id. Trusting a wedding id sent by the browser would have been simpler, but it is exactly how cross-tenant leaks happen.

Tokens for guests, sessions for members

Members use server-side sessions in secure, HTTP-only cookies. Guests get high-entropy invitation and gallery tokens, stored only as hashes. Requiring guest accounts would have made access control uniform but would have stopped most guests from ever responding.

Signed direct uploads and MongoDB-backed email jobs

Photos go from the phone straight to Cloudflare R2 through short-lived signed URLs, so large files never pass through the application server. Bulk invitations become email jobs in MongoDB that a cron-triggered endpoint claims atomically and sends in small batches with bounded retries. Redis and a worker fleet would scale further, but are not needed at this size.

Results

  • The architecture, database and API designs are complete, with explicit security tests planned for cross-wedding access, guest tokens and uploads.
  • The application foundation is built: configuration validation, authentication, database and HTTP layers in a typed Next.js codebase with tests.
  • Feature modules are being built on that foundation, module by module.

What I’d do next

  • Finish the guest invitation and RSVP flow end to end, with the cross-wedding isolation tests running in CI.
  • Add rate limiting on sign-in, RSVP and upload endpoints before opening sign-ups.