Noosa
Next.js, Tech lead•2025 – present
Noosa is the English and Indonesian event ticketing platform I founded: organisers create events, attendees register and pay, and organisers check guests in by QR.
A public site that takes payments has to stay fast and open to anonymous visitors while resisting bots and keeping sessions safe, and a team of five had to ship two to four features a month on top of it. I lead that team and built the whole Next.js web app around a backend-for-frontend gateway that owns rate limiting, captcha, caching and auth, so features ship quickly on a secure base.

Open to everyone, closed to bots.
Noosa started from a blank repository. Events had to be browsable by anyone without an account, checkout had to take real payments, and the whole product had to work in English and Indonesian, down to the error messages the backend returns. Session tokens and the backend secret could never reach browser JavaScript.
Organisers needed more than a form: multiple ticket types, custom registration questions, a gallery and a map per event. Sign-in and other key flows were already drawing automated traffic. Real traffic is spiky and growing: monthly visitors rose about 3.3× from January to August 2026, and a single organiser's Instagram post can bring close to 1,900 visitors in a day.
And the team was five people (two product, one designer, one backend developer and me as tech lead) shipping two to four features a month, each going through discovery first. Every security rule had to stay in sync as routes changed, without a second copy of each path waiting to drift.


One gateway, one route table.
- ABackend-for-frontend gateway
The browser never calls the backend directly. A single Next.js route runs every request through rate limiting, captcha scoring, secret injection, a 15-second timeout, public caching, forced logout on 401, server-side error translation and redacted logging. Tokens stay in httpOnly cookies. The cost is one extra hop per call; the alternative was spreading those checks across services and hoping they agreed.
- BOne typed route table drives every policy
Each backend route is declared once. The same definitions build request URLs and feed the rate-limit tiers, captcha routes, cache allowlist and log redaction, and around 140 API type files are generated from the backend's OpenAPI spec. Renaming a route is a one-line change, and every policy follows it.
- CRate limits keyed by user, not IP
Four Redis tiers, from 5 to 60 requests a minute. Signed-in users are keyed by a hash of their token, so people sharing one office or NAT address don't lock each other out. If Redis is down the limiter fails open and logs loudly: availability over strict enforcement, as a deliberate choice.
- DCheckout state lives in the URL
Multi-ticket, multi-attendee registration is held in the query string, so a checkout is shareable and survives a refresh without a client store. Hand-edited URLs are clamped to the same purchase limits the interface enforces.
- EHover prefetch and server hydration
Event cards prefetch on hover rather than on sight, and route layouts load data on the server and hydrate TanStack Query, so going from a list to an event opens with no loading state. The same pattern runs across 14 layouts. Prefetching every visible card would have meant a server render per card.


The parts nobody notices until they break.
- Event builder. Cross-field validation (price only for paid tickets, capacity never below existing registrations) with backend errors mapped onto the exact field. Questions and their options drag-reorder at two nested levels, and the flow works from the keyboard.
- Type-safe translations. Every translation key is checked at compile time, around 1,300 lines per locale. A typo fails the build instead of showing a raw key to a customer.
- A lean rich-text editor. Stored content round-trips exactly, links are limited to http and https, and read-only views render without loading the editor at all.
- Hardened Google sign-in. A single-use state cookie and an open-redirect guard.
- Hosting migration. Moved from AWS EC2 with Docker Compose and Caddy to Vercel for the web app and Google Cloud Run for the backend.
- Small product calls. An ambiguous "Is event private" switch became a "Visibility: Public / Private" control, and a one-pixel button jump in the loading state was fixed.

Shipping monthly, on a base that holds.
Noosa now serves over 17,000 users, with more than 24,000 tickets sold across 800+ events. reCAPTCHA Enterprise has turned away around 2,000 malicious bot requests before they reached the backend.
The team ships two to four features a month, each one validated with questionnaires, user interviews and mockups before it is built. Because security and caching live in one gateway and one route table, new features inherit them without extra work. Adoption data steers the roadmap too: Sign in with Google is used by roughly 9% of users, which set the next priority for authentication.
Reach in 2026 so far: more than 80,000 visitors and 450,000 page views across 1,400+ event pages, with more than 60 Indonesian cities sending 100+ visitors each. Distribution is organiser-led: Instagram brings 37% of first visits and link-in-bio tools another 8%, against 1.6% from Google search. The product spreads through organisers' own channels, not SEO.
Next on the list: on-demand token refresh, an atomic rate-limit counter, PKCE for Google sign-in, and tests for the gateway.
Noosa also delivers client work on the same foundations: Alumni United 2026 and PGA ITS were both built under it.
- Year2025 – present
- IndustryEvent ticketing
- ClientNoosa · my own company
- RoleFounder · tech lead · built the entire web app · team of 5
- Scope of work/ Frontend / Platform / Infrastructure / CI/CD
- StackNext.js · React · TypeScript · TanStack Query · Tailwind CSS · Redis · reCAPTCHA Enterprise · next-intl · Zod · Vercel · Google Cloud Run · GitHub Actions · Cloudflare