Skip to content

Browser (PKCE)

@spotify-effect/browser provides OAuth PKCE flow support for browser-based applications. PKCE eliminates the need for a client secret, making it safe for frontend-only applications.

Installation

sh bun add @spotify-effect/browser @spotify-effect/core effect

Setup

Create a SpotifyBrowser instance with the required options:

import { SpotifyBrowser } from "@spotify-effect/browser";
import * as Layer from "effect/Layer";
const spotify = SpotifyBrowser.layer({
clientId: "your-client-id",
redirectUri: "https://yourapp.com/callback",
session: {
sessionStorage: window.sessionStorage,
localStorage: window.localStorage,
history: window.history,
},
});
export const SpotifyLayer = spotify;

OAuth Flow

  1. Start the login flow

    Generate the authorization URL with PKCE challenge:

    import { Effect, Layer } from "effect";
    import { SpotifyBrowser } from "@spotify-effect/browser";
    const program = Effect.gen(function* () {
    const spotify = yield* SpotifyBrowser;
    // Generate URL and redirect
    const url = yield* spotify.auth.startPkceLogin({
    scopes: ["user-read-private", "user-library-read", "playlist-read-private", "streaming"],
    });
    window.location.href = url;
    });
    Effect.runPromise(program.pipe(Effect.provide(SpotifyLayer)));
  2. Handle the callback

    On your callback page, exchange the authorization code for tokens:

    import { Effect } from "effect";
    import { SpotifyBrowser, readAuthorizationCallback } from "@spotify-effect/browser";
    const callback = readAuthorizationCallback(new URL(window.location.href));
    if (callback) {
    const program = Effect.gen(function* () {
    const spotify = yield* SpotifyBrowser;
    const tokens = yield* spotify.auth.exchangeCode(callback.code);
    console.log("Authenticated!", tokens.accessToken);
    // Tokens are automatically stored in session/localStorage
    });
    Effect.runPromise(program.pipe(Effect.provide(SpotifyLayer)));
    }
  3. Access the API

    Use any Spotify service after authentication:

    const program = Effect.gen(function* () {
    const spotify = yield* SpotifyBrowser;
    const user = yield* spotify.users.getCurrentUserProfile();
    console.log(`Hello, ${user.display_name}!`);
    });

Session Management

Check authentication status

const tokens = spotify.auth.getTokens();
if (tokens) {
// User is authenticated
const expiresAt = new Date(tokens.accessTokenExpiresAt);
console.log(`Token expires at: ${expiresAt}`);
}

Automatic token refresh

Tokens are automatically refreshed when the browser client has both a refreshToken and an accessTokenExpiresAt value. That applies to tokens returned from exchangeCode, tokens restored from storage, and tokens provided through setTokens. Call refreshToken to proactively refresh:

const program = Effect.gen(function* () {
const spotify = yield* SpotifyBrowser;
const tokens = yield* spotify.auth.refreshToken(currentRefreshToken);
console.log("Tokens refreshed!");
});

Logout

spotify.auth.logout();
// Clears tokens from browser storage and the live in-memory session

Available Services

The browser package provides all the same services as @spotify-effect/core:

  • albums — Get albums, tracks, and album metadata
  • artists — Get artists, albums, and related artists
  • browse — Featured playlists, categories, new releases
  • follow — Follow/unfollow artists and users
  • library — Saved albums and tracks
  • markets — Available markets
  • personalization — Top artists and tracks
  • player — Playback control (requires streaming scope)
  • playlists — Manage playlists
  • search — Search the catalog
  • tracks — Track audio features and analysis
  • users — User profiles

Example: Build a simple player

import { Effect } from "effect";
import { SpotifyBrowser } from "@spotify-effect/browser";
const program = Effect.gen(function* () {
const spotify = yield* SpotifyBrowser;
// Get user's top tracks
const topTracks = yield* spotify.personalization.getMyTopTracks({
limit: 10,
});
// Start playback
const uris = topTracks.items.map((track) => track.uri);
yield* spotify.player.play({ uris });
});
Effect.runPromise(program.pipe(Effect.provide(SpotifyLayer)));

Next steps