I wanted a portfolio project that showed something harder than a CRUD: a real multi-tenant architecture. I picked a Linktree-style link page because it is small, makes sense in ten seconds and forces you to solve what matters: every person gets their own address, their own data and their own look.
The result is Broslunas Link: each page lives on its own subdomain, for example marta.broslunas.link. It is a free demo; it does not process payments.
§ 01One subdomain per person
DNS has a wildcard record pointing at Vercel and a proxy.ts reads each request's host. If it is marta.broslunas.link, it rewrites internally to /tenant/marta without changing the visible URL; app. serves the dashboard and the root serves the landing page. There is one app and one database, not one deployment per customer.
Each sign-up also registers the subdomain in Vercel through its API so it issues the certificate. If that step fails the page is still created, and nothing is lost.
§ 02Isolation does not rely on memory
The rule is simple: no query is written without tenantId. So that it does not depend on remembering, all data access goes through forTenant(id), which returns links, theme and events already scoped to that tenant. A test creates two tenants and checks that no read or write crosses data. If someone breaks the rule, CI fails.
§ 03Fast because it does almost nothing
The public page is cached with one tag per tenant (tenant:<slug>) and invalidated only when its owner saves a change. Between saves it is static content. Views and clicks are recorded separately, with no cookies, to give simple per-link stats.
§ 04Security in the small things
- +Links only accept
http,https,mailtoandtel; ajavascript:link never gets saved. - +Embedded videos only come from YouTube and Spotify, and the user's URL is never used as the
src: it is converted to the player's. - +Slugs are validated by format and against a reserved list (
www,app,admin,api…). - +Sign-up and creation are rate limited, stored in MongoDB with a TTL index so as not to add Redis just for this.
§ 05What users see
Seven preset themes, font, colour and card-style tweaks, a WhatsApp button with a starting message, a downloadable QR as PNG or SVG, and an optional custom domain. The landing page's motion graphics, and this project's, are made with Remotion.
§ 06What I take from it
Naming the hard part —isolation— and fencing it in with a test makes everything else go fast. I built it in a single day with 65 passing tests, and the tenantId access layer is already the base for the next project: a booking system.
You can see it at broslunas.link and read the full case study on the project page.
■ End of post↑ Back to top
