Inspiration
Finding someone to play sports with can be surprisingly hard. You either rely on a group chat that goes quiet, show up and hope someone's free, or just end up playing alone. Dating apps solved the "find someone nearby with shared interests" problem for relationships, now we wanted to solve it for sport. The idea was simple: what if you could look through players near you, filter by sport and skill level, and send a match request in one tap? That's Sinder.
What it does
Sinder is a sports matchmaking web app that connects players based on sport, skill level, and location. After creating a profile you can browse nearby players on a card-based discover feed, send match requests, and browse open game posts where people are actively looking for a partner for a specific time and place. When someone accepts your request, both players get an email notification. A reminder email goes out automatically 24 hours before the confirmed game. The whole flow of finding a player to showing up on court is handled in one place.
How we built it
*Frontend *— React with Vite, styled entirely in Tailwind CSS with custom design tokens. React Router v7 for navigation, React Context for auth and toast notifications.
Backend — Node.js with Express, JWT-based authentication, and a clean REST API. We used SQLite locally via a custom pg-compatible wrapper so the same query code runs against PostgreSQL (Aiven) in production without changes.
Data model — A single requests table handles both direct player match requests and session-join requests (distinguished by a post_id column). A separate friendships table tracks the social graph. Relationships move through pending → accepted, with full delete-on-unmatch so players can re-request cleanly.
Challenges we ran into
The relationship uniqueness constraint was the trickiest part to get right. A naive approach would let Player A send a request to Player B, and Player B send a separate request back to Player A, thus creating two separate rows for the same pair. We solved this with a bidirectional unique index using LEAST and GREATEST on the two user IDs so the pair is always normalized regardless of direction, and scoped it to only active statuses so unmatched players can re-connect.
Accomplishments that we're proud of
As most people have teams of 4, we were just two friends with a dream.
What we learned
How much of a real-world app lives in the edges rather than the happy path. Preventing duplicate match requests, handling token expiry gracefully, ensuring field names match exactly between the form and the backend. None of these are hard individually, but together they are where most of the debugging time went. We also learned that managed infrastructure like Aiven removes a huge amount of operational complexity early on, letting us focus on product logic rather than database configuration. Also that defining a clear API contract before building the frontend saves a lot of back-and-forth later.
What's next for Sinder
In-app messaging between players so the conversation doesn't have to move to other platforms. Real location services using the browser's Geolocation API feeding into PostGIS proximity queries so "nearby" means actually nearby. Club and group play support for organizing sessions with more than two players. And a native mobile app because finding a sports partner is something people do on their phone, not their laptop.
Built With
- aiven
- css
- javascript
- postgresql
- react
- vscode
Log in or sign up for Devpost to join the conversation.