Spotify Statistic

Next.jsSpotify APIServer-Sent EventsOAuth 2.0
Spotify Statistic hero image

Building a Real-time Spotify Stats Dashboard with Next.js & SSE

The Challenge

Every music lover is curious about their listening habits, but many third-party apps require extensive permissions and can be slow to update. My goal was to create a personal dashboard that could show real-time listening activity. This presented three core challenges:

  1. Real-time Data: The official Spotify API does not support WebSockets or Webhooks for real-time playback updates. The most obvious solution, short polling, is inefficient and resource-intensive.
  2. Credential Security: The Spotify API client_id and client_secret must be kept secure. Exposing them on the client-side is not an option.
  3. Infrastructure Cost: Building a traditional dedicated backend server just for this personal project would be overkill and costly.

The Solution

I architected a solution using Next.js as a full-stack framework. The key was to leverage Next.js API Routes to create a lightweight, serverless backend that acts as a secure proxy to the Spotify API. For the real-time component, I opted for Server-Sent Events (SSE) to create a persistent, one-way connection that efficiently pushes data from my server to the client without the overhead of constant polling.

Technical Architecture

Next.js API Route as a Secure Proxy

The entire backend logic lives within the /app/api directory in my Next.js project. This serverless function handles the complex OAuth 2.0 flow and all communication with the Spotify API. The client-side application never interacts with Spotify directly; it only talks to my API route. To solve the real-time data problem, I created an API route that establishes an SSE connection. The server polls the Spotify API internally at a reasonable interval.

// /app/api/spotify-data/route.ts
// A simplified example of the proxy endpoint

export async function GET(req: Request) {
    try {
        const interval = 5000

        const stream = new ReadableStream({
            start(controller) {
                const intervalId = setInterval(async () => {
                    const datasource = await spotifyData()
                    const data = `data: ${JSON.stringify(datasource)}\n\n`
                    controller.enqueue(new TextEncoder().encode(data))
                }, interval)

                req.signal.onabort = () => {
                    clearInterval(intervalId)
                    controller.close()
                }
            },
        })

        return new Response(stream, {
            headers: {
                'Content-Type': 'text/event-stream',
                'Cache-Control': 'no-cache, no-transform',
                Connection: 'keep-alive',
            },
        })
    } catch (error) {
        // using axios to fetching spotify data
        const data = error instanceof AxiosError ? error.response?.data : undefined
        const message = error instanceof AxiosError ? error.message : error
        const statusCode = error instanceof AxiosError ? error.response?.status || 500 : 500

        return new NextResponse(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }, status })
    }
}

Results

The implementation achieved:

  • Secure Authentication: Spotify API credentials are never exposed to the client.
  • Real-time Experience: The UI updates automatically within seconds of a song change.
  • Efficient Architecture: A single, persistent SSE connection replaces dozens of inefficient polling requests from the client.
  • Zero Infrastructure Cost: The entire application is deployed on Vercel's free tier, leveraging their serverless function capabilities.

Technical Challenges Overcome

Managing the OAuth 2.0 Flow

Handling Spotify's authentication, especially the process of obtaining and using refresh tokens to request new access tokens on the server-side, was a key challenge that required careful state management.

Graceful Connection Handling

Ensuring the SSE connection was stable, handled errors gracefully, and terminated properly when the user closed the browser tab was crucial to prevent memory leaks and orphaned server processes.

API Rate Limiting

The backend proxy was designed to be mindful of Spotify's API rate limits, ensuring my server-side polling was frequent enough to feel "real-time" but not so frequent as to get blocked.

Conclusion

This project was a deep dive into building a modern, serverless, real-time web application. It demonstrates how Next.js API Routes can be leveraged to create powerful, secure, and cost-effective backends without leaving the comfort of the React ecosystem.

One of the most valuable takeaways came from a limitation: the Spotify API doesn't support real-time updates. At first, I considered short polling, making the client repeatedly ping the server. It worked, but it was inefficient. That's when I discovered Server-Sent Events (SSE), and it completely shifted how I approach data flow and resource management. I learned that there is no perfect solution in the backend, only trade-offs. The success of this implementation proves that by understanding those trade-offs, we can architect elegant solutions to complex problems.