I wanted a clean music app with no ads, no subscription walls, and the ability to actually download songs. So I built the full stack - a middleware layer, API surface, and a proper web client on top.
Features
- Search songs, albums, and playlists
- Stream directly in the browser
- Download tracks for offline use, cached in IndexedDB
- Playlist management with drag-and-drop reordering
- Login to save your library across devices
- Server-side data fetching with TanStack Query
Key decisions
- Three-layer architecture (client → middleware → data source) — the middleware layer insulates the client from the upstream data shape; if the source changes, only the middleware needs updating and the client remains unchanged
- IndexedDB over the Cache API for downloaded tracks — IndexedDB gives structured, queryable storage with larger quotas than localStorage; the Cache API is designed for HTTP responses, not binary audio blobs that need to be looked up by track ID
- Zustand for player state, TanStack Query for server state — keeping them separate avoids the temptation to put server data in the player store; TanStack Query handles staleness and refetching, Zustand handles playback position and queue without coupling
Future directions
- Export and import playlists
- Recommendation system based on listening history
Built with
- Next.js
- Drizzle ORM + Neon - PostgreSQL
- Better Auth - authentication
- Zustand - client state
- TanStack Query - server state
- tailwindcss
- Deployed on Vercel