The Spark
Booking a tee time in the Twin Cities is a part-time job.
You open one course's site, find nothing in your window. Open another. Then another. Some have native booking pages, others use third-party platforms. By the time you've checked five courses, you've spent 15 minutes and learned that everything in your radius is booked — or that there's a 6:40 AM slot at a course you didn't think to check.
We're golfers. We live in Minneapolis. We've had this exact morning more times than we'd like to admit. And the more we thought about it, the more obvious it became: somebody should just build the aggregator.
Nobody had. So we did.
The Market Gap
The Twin Cities metro has over 100 public golf courses — a remarkably dense market for a region this size. But course discovery is fragmented in a way that's almost unique to golf. Other consumer verticals consolidated years ago: restaurants have OpenTable, hotels have Booking.com, salons have Yelp. Golf? Each course runs its own booking page, often through a different platform.
That fragmentation creates a real pain point. Golfers don't search for a specific course — they search for an available time. "Can I play somewhere reasonable on Saturday morning?" is the actual question. No existing tool answered it for our area.
That's a buildable market gap: a clear, repeating consumer pain, no incumbent solving it locally, and a defined geography where coverage is achievable. Hyperlocal apps don't have to scale to millions of users to be successful — they need to be deeply useful to the people in one place.
What We Built
TeeSheet TC is a free Flutter app, live on both the App Store and Google Play. Open the app, pick a date and time window, set a search radius, and you see every available tee time across 90+ Twin Cities courses — sorted by price or distance.
Tap a tee time and the app deep-links you straight into the course's own booking flow. We never touch payments, never act as a middleman. The booking happens on the course's existing infrastructure. We're just the search layer that gets you there.
The core features are intentionally narrow:
- Date + time window search — most golfers know roughly when they want to play
- Radius filter — sorted by distance from the user's location
- Price filter — sorted ascending, because price is the second-most-common filter golfers care about
- Favorites — saved courses for quick re-checking
- Course detail pages — basic info, location, deep links to course websites
Everything else is deliberately omitted. No social feed, no scorekeeping, no league management, no swing analysis. The app does one thing and gets out of the way.
Tech Choices
Mobile: Flutter + Riverpod
We chose Flutter for the mobile app for one practical reason: we wanted to ship to both iOS and Android with one codebase, without compromising on UI polish or performance. React Native was the obvious alternative, but Flutter's rendering model — drawing every pixel itself rather than wrapping native components — gave us tighter control over animations, scrolling performance, and consistent visual behavior across platforms.
For state management we used Riverpod. After working with several Flutter state-management approaches on previous projects, Riverpod's compile-time safety and provider-based architecture made it the easy choice. Search filters, course data, location, favorites, and user settings each live in their own provider — easy to test, easy to reason about, no boilerplate.
Backend: Node.js + Express + TypeScript
The backend is a TypeScript service running on Railway. It exposes a small, focused API that the mobile app consumes — primarily a single endpoint that takes a date, time window, location, and radius, and returns matching tee times sorted by the user's chosen criteria.
TypeScript in strict mode, no any types. The contract between the API and the mobile app is well-defined and stable. When the mobile app expects a TeeTime object with specific fields, the backend's type system guarantees it.
Database: PostgreSQL with earthdistance
Geo search is the interesting part. "Find all available tee times within a 15-mile radius of the user, sorted by distance" is a classic query that's easy to write badly and hard to write well at scale.
We used PostgreSQL's earthdistance extension, which lets us compute great-circle distances between two lat/lng points natively in the database. Combined with a cube index on the course coordinates, a radius search becomes a single SQL query that runs in milliseconds even as the data grows.
The whole search — date filtering, time window, radius, course availability, sorting — is wrapped in a single Postgres function that the API just calls. Less code in the application layer, faster response times, less to go wrong.
Hosting: Railway
The API and database both run on Railway. We've written about why we like Railway for full-stack apps — it's the right tool when your project needs more than static hosting but you don't want to deal with AWS-level infrastructure complexity.
For TeeSheet TC, Railway gave us: a managed Postgres database with the earthdistance extension available, an API server deployment from the same repo, environment variable management, and automatic deploys on git push. Total time from "we have a working backend locally" to "it's live in production" was under an hour.
The Launch Path
We shipped Android first, in early 2026. Google Play's review process is more forgiving than Apple's, and getting the app into golfers' hands quickly was more valuable than waiting for both platforms to be ready simultaneously. We could iterate on user feedback, refine the search experience, and fix the issues you only find when real users start using the app.
iOS came later. Apple's review process is famously stricter, and we wanted the iOS experience to be polished before submitting. The Flutter codebase made the technical port trivial — but App Store Review surfaced its own set of friction points that had to be addressed before approval. Once approved, the app was live on the App Store and both platforms have been in sync ever since.
The two-stage launch turned out to be the right call. By the time iOS launched, we'd already had months of real Android usage informing what the app should look like.
What This Project Showed Us
TeeSheet TC is, on the surface, a small consumer app. But it touches almost every part of what we do as a studio:
- Mobile development for both iOS and Android, from one codebase
- Backend engineering with a typed API, designed for a specific consumer flow
- Database design that uses Postgres's extensions properly rather than reaching for a specialty service
- DevOps on a modern hosting platform with zero-config deploys
- Product thinking — choosing what not to build, knowing your user, shipping the narrowest useful version first
This is the kind of work we like doing. A real problem, a defined audience, a clean technical stack, and a real launch. No invented complexity, no padding, no half-built features.
What's Next
The app is live and growing. We're watching usage patterns to decide what to build next — push notifications when a favorite course opens up new times, save/restore searches, expansion to neighboring metros. The core remains: one search, every available time, deep-linked to the course's own booking flow.
If you have a hyperlocal market that's still fragmented — golf courses, dive shops, fitness studios, anything where consumers want availability search across many small operators — the playbook from TeeSheet TC applies cleanly. We'd love to talk about what you're trying to build.