Skip to content

Eternal Vow

Full-stack, tRPC•2024–2026

Eternal Vow is a digital wedding-invitation platform for the Indonesian market, and every wedding is effectively its own small website, with its own guests, media, copy and design.

One developer had to serve 105 of them from a single codebase, keep pages fast under event-day traffic spikes, and let non-developers launch each new invitation without touching code.

Eternal Vow: platform home
Challenge

A hundred and five websites, one developer.

The platform had to be multi-tenant from day one: 105 invitations across 8 client-designed themes, all on one codebase. The client supplied the theme designs, photos and copy; everything that turns them into working sites was mine. Guests are anonymous visitors, but RSVPs and guestbook wishes must be gated per invited guest: one wish per guest, no accounts, no spam.

Traffic is spiky in the worst way. An invitation goes out and thousands of guests hit the same page within days; over the platform's life that added up to more than 60,000 sessions. The busiest month brought 4,100 visitors, the busiest day 492 new visitors, and about a third of daily visitors were coming back. The pages are heavy with galleries, music and video, opened mostly on Indonesian mobile networks.

And after handoff there is no developer in the loop: non-technical agency staff create, configure and activate every invitation: a roughly 45-field template plus nested subevents, gift banks, galleries and copy. The couples themselves are non-technical too, and they lose credentials; the agency needed a way to re-show them later, a recoverability-versus-security trade-off that had to be designed deliberately rather than stumbled into.

105
Client invitations shipped
13,250
Event guests served
2,257
Guestbook wishes sent
8
Invitation designs, one route
A live invitation cover, personalised to the invited guest
A live invitation cover, personalised to the invited guest
On mobile: cover, countdown and the per-guest RSVP form
On mobile: cover, countdown and the per-guest RSVP form
Approach

One route, one schema, one cache, for every wedding.

  • A
    One catch-all route serves every invitation

    A single dynamic route fetches the invitation by slug and maps a type field to one of eight template implementations; inactive or missing slugs redirect away. The same route serves demo pages from mock data with client-side-only RSVP, so prospects can try every design without touching the database. The alternatives (a page per invitation, or a route per design) were rejected as unmaintainable at 105 tenants.

  • B
    Per-guest secured links

    Each guest gets a unique key per invitation, and their personal link carries it. The server resolves the guest by a composite unique key; RSVP and wish forms only render and submit for a resolved guest, and wishes are unique per guest. Keys are generated in the dashboard with normalization and collision suffixes: no accounts for guests, but no anonymous spam either.

  • C
    A Redis read-through cache, invalidated cross-app

    Invitation data and metadata are cached per slug for an hour in front of PostgreSQL on the hot route, and the admin CMS purges exactly those keys on edit, activate and deactivate. Session validation is cached too. Event-day spikes hit the cache; edits still show up instantly. The cost: two apps must agree on key names, kept in one shared convention.

  • D
    The admin CMS as one deep form

    Fourteen section sub-forms configure the ~45-field template row plus all its nested child records in one flow; edits delete and recreate the children and purge the web app's cache keys. The agency launches a complete invitation without a developer. Separate CRUD screens per record type would have meant too many steps for non-technical staff.

  • E
    A four-stage media pipeline

    Client-side compression, then server-side WebP conversion, then a CDN on object storage, then Cloudflare image resizing through a custom image loader. Each stage cuts bytes before the next, which is what keeps gallery-heavy pages usable on mobile networks. Pending uploads and deletes are tracked in a client store.

Gallery: the couple's moments
Gallery: the couple's moments
RSVP and wishes, gated to the resolved guest
RSVP and wishes, gated to the resolved guest
Couple introduction in Indonesian copy
Couple introduction in Indonesian copy
Details

Two apps, run like a product.

  • Two role-separated auth systems on one user table. An admin/client role enforced in each app's API middleware, separate session cookies, and client login behind an invisible captcha verified server-side.
  • Recoverable credential issuing. Login uses a standard hash, and the agency can re-show credentials to couples who lose them, a deliberate, contained trade-off for a non-technical clientele.
  • Hand-rolled bilingual copy. Per-design, per-language copy with fallback, plus a 30-value set of per-client text overrides in the database, with no i18n library.
  • Ops without drama. 39 schema migrations, 10 hand-written data migrations and 12 dated backups over the platform's life.
  • Optimistic UI. Wishes appear instantly, and bulk guest import takes one name per line with temporary IDs and rollback on error.
Outcome

Twenty months of live traffic, no developer in the loop.

The agency launched every new invitation from the CMS; after handoff, no developer was needed to ship a wedding. One codebase carried 105 tenants through about 20 months of live traffic, from November 2024 to June 2026, serving 13,250 real guests, who sent 2,257 guestbook wishes through the per-guest link system. The same system ended up carrying more than weddings: engagements, sangjit, khitan and graduation invitations, 139 tracked pages in all.

Analytics logged 60,895 sessions and 93,845 page views over that period. The same guest typically opens an invitation on two or three devices, so the 13,250 headcount is the honest people-number.

Almost all of that traffic arrived through shared links: direct visits made up 66% of first visits, Meta 14%, link-in-bio tools 10% and Instagram 9%. Guests came from 624 cities, with Jakarta the largest at 38%.

  • Year2024–2026
  • IndustryDigital wedding invitations
  • RoleSole full-stack developer · all infrastructure · via HREF.ID
  • Scope of work/ Full-stack / Platform / CMS / Infrastructure
  • StackNext.js · React · TypeScript · tRPC · Prisma · PostgreSQL · Redis · Lucia Auth · Tailwind CSS · Framer Motion · sharp · DigitalOcean Spaces · Cloudflare · Vercel · Railway
Elian Richard