"Jammming" Spotify Playlist Submission

Hey coders. Would love for you to check out my Spotify playlist creator submission and let me know what you think. It was my first time incorporating API into a project as well as my first time converting a React project into a web app on GitHub, but gladly it all came together.

GitHub Link
Web App Link

1 Like


So I tried testing the webapp but it wasn’t possible to search for any songs. I kept getting a 403 error.

I was also going to review your code but it’s also not possible to do that as it’s all been compiled.

Hey. Apparently I have to add your email to my Spotify developer app to give you access to use it properly

I am currently working on this project. But I have an issue retrieving the access token, I am using the authorisation code with PKCE flow and not the implicit grant flow. Yet, it is quite simple, I followed the instructions given here Authorization Code with PKCE Flow | Spotify for Developers. I think I have done everything as instructed but I still have this error in the console :
POST https://accounts.spotify.com/api/token 400 (Bad Request). Here is my code :

// spotify.js const CLIENT_ID = "0697d4488dd2433d9bc4c8d3e4d9016d"; const REDIRECT_URI = "http://localhost:3000"; const API_URL = "https://accounts.spotify.com/api/token"; export async function getToken(code) { const verifier = localStorage.getItem("verifier"); const params = new URLSearchParams(); params.set("grant_type", "authorization_code"); params.set("code", code); params.set("redirect_uri", REDIRECT_URI); params.set("client_id", CLIENT_ID); params.set("code_verifier", verifier); const payload = { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'authorization_code', code, redirect_uri: REDIRECT_URI, client_id: CLIENT_ID, code_verifier: verifier, }), } try { const response = await fetch(API_URL, payload); if(response.ok) { const data = await response.json(); return data; } else { console.error("Error exchanging code for token : ", await response.text()); return null; } } catch (error) { console.error("Catch an error exchanging code for token : ", error); return null; } } export async function redirectToAuthCodeFlow() { const verifier = generateCodeVerifier(128); const challenge = await generateCodeChallenge(verifier); localStorage.setItem("verifier", verifier); const params = new URLSearchParams(); params.append("response_type", "code"); params.append("client_id", CLIENT_ID); params.append("redirect_uri", REDIRECT_URI); params.append("scope", "playlist-modify-private playlist-modify-public user-read-private"); params.append("code_challenge_method", "S256"); params.append("code_challenge", challenge); window.location.href = `https://accounts.spotify.com/authorize?${params.toString()}`; } function generateCodeVerifier(length) { const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const values = crypto.getRandomValues(new Uint8Array(length)); return values.reduce((acc, x) => acc + possible[x % possible.length], ""); } async function generateCodeChallenge(codeVerifier) { const data = new TextEncoder().encode(codeVerifier); const digest = await window.crypto.subtle.digest('SHA-256', data); return btoa(String.fromCharCode(...new Uint8Array(digest))) .replace(/=/g, '') .replace(/\+/g, '-') .replace(/\//g, '_'); } export async function getTracks(searchTerm) { let accessToken = localStorage.getItem('access_token'); //let expiresAt = localStorage.getItem('expires_at'); const url = `https://api.spotify.com/v1/tracks/search?q=${encodeURIComponent(searchTerm)}&type=track`; const headers = {'Authorization' : `Bearer ${accessToken}`}; try{ const response = await fetch(url, {headers}); if(response.ok){ const jsonResponse = await response.json(); const tracks = jsonResponse.map((item) => ({ id: item.id, name: item.name, artist: item.artists[0].name, album: item.album.name, uri: item.uri, })); return tracks; } return []; }catch(error){ console.log("Error fetching tracks : " + error); return []; } }


// App.js import React, { useEffect, useState } from "react"; import './App.css'; import SearchBar from "./components/SearchBar"; import Results from "./components/Results"; import Playlist from "./components/Playlist"; import { getToken, redirectToAuthCodeFlow, getTracks } from "./spotify"; function App() { const [searchResults, setSearchResults] = useState([]); useEffect(() => { async function getPermission(){ const params = new URLSearchParams(window.location.search); const code = params.get("code") || localStorage.getItem("code"); if (!code) { redirectToAuthCodeFlow(); } else { localStorage.setItem("code", code); const token = await getToken(code); if(token){ localStorage.setItem("accessToken", token.access_token); localStorage.setItem("expiresAt", Date.now() + token.expires_in); } } } getPermission(); }, []); const onSearch = (searchTerm) => { getTracks(searchTerm) .then((tracks) => setSearchResults(tracks)); } return ( <> <header className="header"> <SearchBar onSearch={onSearch}/> </header> <main className="main"> <Results searchResults={searchResults}/> <Playlist/> </main> </> ); } export default App;

It’s been days that I am trying to solve the problem. I would appreciate you help.