Skip to content
← Projects

BadmintonSG

A mobile-first board where Singapore badminton players resell balloted court slots and find players for their games, with no accounts and no payments.

  • Next.js
  • TypeScript
  • Prisma
  • PostgreSQL
  • Tailwind CSS

Problem

Badminton courts in Singapore are balloted weeks ahead, whether at ActiveSG halls, community centres, or school sports halls. Win a slot you can no longer use and your only option is to post it into a Telegram group, where it sinks within hours while buyers scroll around asking if anything is still available. Hosts who have a court but not enough players deal with the same mess. Both sides just need a board they can filter by date and area.

What I built

BadmintonSG has two boards. On the courts side, a seller posts a slot with the venue, date, time, and price; a buyer filters the board, opens a listing, and reveals the seller's contact, which can be a phone number with one-tap call and WhatsApp links, a Telegram handle, or both. On the players side, a host posts a game with a skill-level range, how many players it still needs, and the court's total headcount, and players find it the same way. The date, region, and skill filters are all multi-select, so "this Saturday or Sunday, Central or West" is a single filter pass, and picking a range on the calendar highlights every day in between the way a flight-booking picker does. Every listing also links out to its venue on Google Maps.

There are no accounts: posting instantly gives you a private manage link, and the app makes you copy it before showing the edit controls, since that link is the only way to adjust player counts, mark a post sold, or pull it down later. Listings expire on their own once the slot's start time passes. Payment and coordination stay off the platform on purpose. The goal is the shortest possible path from "I have a court" to "here is a phone number".

Screenshots

Courts board grouped by date with region and time filtersPlayers board showing skill level and players neededPost a court slot formFAQ page

The courts board, the players board, the posting form, and the FAQ, all built for one hand on a phone.

Tech and approach

Next.js 15 App Router with strict TypeScript, Prisma 6 on a Neon Postgres database, and Tailwind for the mobile-first UI, since most people use it courtside on a phone. Every database query lives in a services layer; route handlers only validate input with Zod and call a service. That boundary is also the privacy mechanism: public queries structurally cannot select contact details or manage-link tokens, and the reveal endpoint re-checks a post's status server side rather than trusting the UI. Rate limiting runs on Postgres with hashed IPs, which avoids adding Redis, and all court times are SGT wall-clock handled in a single dayjs module. Vercel functions are pinned to Singapore so the app sits in the same region as its database and its users. Tested with Jest, React Testing Library, and a Playwright run of the core listing-to-contact loop; the Vercel and Neon free tiers host the whole thing.