Jammming project mystery - search doesn't work

Project link: Jammming

Hi, I have completed all the steps in Jammming. I really struggled & copied the walkthrough video step by step but the search still isn’t working. When I inspect the app, the error message in the console is:

GET https://api.spotify.com/v1/search?type=track&q=[whatever I’ve input] 400 Spotify.js:28

Below I’ve starred in each component where the console is telling me the problems are arising.

Spotify.js:

const clientId = [redacted];
const redirectUri =  "http://localhost:3000/";

let accessToken;

const Spotify = {
    getAccessToken() {
        if(accessToken){
            return accessToken;
        }
        const accessTokenMatch = window.location.href.match(/access_token([^&]*)/);
        const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);

        if (accessTokenMatch && expiresInMatch) {
            accessToken = accessTokenMatch[1];
            const expiresIn = Number(expiresInMatch[1]);
            window.setTimeout(() => accessToken = '', expiresIn * 1000);
            window.history.pushState('Access Token', null, '/');
            return accessToken
        } else {
            const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`;
            window.location = accessUrl;
        }
    },

    search(term) {
        const accessToken = Spotify.getAccessToken();
        **return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`,{**
           headers: {
               Authorization: `Bearer ${accessToken}`
            }
       }).then(response => {
           return response.json();
       }).then(jsonResponse => {
           if(!jsonResponse.tracks) {
               return [];
           }
           return jsonResponse.tracks.items.map(track => ({
               id: track.id,
               name: track.name,
               artists: track.artists[0].name,
               album: track.album.name,
               uri: track.uri 
           }));
       }); 
    },

    savePlaylist(name, trackUris) {
        if (!name || !trackUris.length) {
            return;
        }
        const accessToken = Spotify.getAccessToken();
        const headers = { Authorization: `Bearer ${accessToken}`}
        let userId;
        return fetch('https://api.spotify.com/v1/me', {
            headers: headers
        }).then(response => {
            return response.json();
        }).then(jsonResponse => {
            userId = jsonResponse.id;
            return fetch(`https://api.spotify.com/v1/users/${userId}/playlists`, {
                headers: headers,
                method: 'POST',
                body: JSON.stringify({ name: name})
            }).then(response => {
                return response.json();
            }).then(jsonResponse => {
                const playlistId = jsonResponse.id;
                return fetch(`https://api.spotify.com//v1/users/${userId}/playlists/${playlistId}/tracks`, {
                    headers: headers,
                    method: 'POST',
                    body: JSON.stringify({ uris: trackUris})
                })
            })
        })
    }
}

export default Spotify;

App.js:

import React from 'react';
import './App.css';

import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';
import Spotify from '../../util/Spotify';

class App extends React.Component{
  constructor(props) {
    super(props)

    this.state = {
      searchResults: [],
      playlistName: 'My Playlist',
      playlistTracks: []
    };
    this.addTrack = this.addTrack.bind(this);
    this.removeTrack = this.removeTrack.bind(this);
    this.updatePlaylistName = this.updatePlaylistName.bind(this);
    this.savePlaylist = this.savePlaylist.bind(this);
    this.search = this.search.bind(this);
  }

  addTrack(track) {
    let tracks= this.state.playlistTracks;
    if(tracks.find(savedTrack => savedTrack.id === track.id)) {
      return;
    }
    tracks.push(track);
    this.setState({playlistTracks: tracks})
  }

  removeTrack(track) {
    let tracks= this.state.playlistTracks;
    tracks = tracks.filter(currentTrack => currentTrack.id !== track.id);
    this.setState({ playlistTracks: tracks });
  }

  updatePlaylistName(name) {
    this.setState({ playlistName: name });
  }

  savePlaylist() {
    const trackUris = this.state.playlistTracks.map(track => track.uri);
    Spotify.savePlaylist(this.state.playlistName, trackUris).then(() => {
      this.setState({
        playlistName: 'New Playlist',
        playlistTracks: []
      })
    })
  }

  search(term) {
    **Spotify.search(term).then(searchResults => {**
      this.setState({searchResults: searchResults})
    })
  }

  render() {
    return(
      <div>
        <h1>Ja<span className="highlight">mmm</span>ing</h1>
        <div className="App">
          <SearchBar onSearch={this.search} />
          <div className="App-playlist">
            <SearchResults searchResults={this.state.searchResults}
             onSearch={this.search}
             onAdd={this.addTrack} />
            <Playlist playlistName={this.state.playlistName} 
            playlistTracks={this.state.playlistTracks} 
            onRemove={this.removeTrack}
            onNameChange={this.updatePlaylistName}
            onSave={this.savePlaylist} />
          </div>
        </div>
      </div>
    )
  }
}

export default App;

SearchBar.js:

import React from 'react';
import './SearchBar.css';

class SearchBar extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            term: ''
        }
        this.search = this.search.bind(this);
        this.handleTermChange = this.handleTermChange.bind(this);
    }

    handleTermChange(event) {
        this.setState({term: event.target.value});
    }

    search() {
        **this.props.onSearch(this.state.term);**
    }
    
    render() {
        return (
            <div className="SearchBar">
                <input placeholder="Enter A Song, Album, or Artist"
                onChange={this.handleTermChange} />
                <button className="SearchButton" 
                onClick={this.search}>SEARCH</button>
            </div>
        )
    }
}

export default SearchBar;

Any help will be greatly appreciated!

I’m having exactly the same problem. Did you have any luck resolving it?

Hi,
Sorry to be really unhelpful but yes I did resolve it, I just can’t remember how. All of the lines of code that I marked as problems above are unchanged in my working version. I recall something that really helped was automatically generating the access token when the page mounts. It goes in App.js just above the render function:

componentDidMount() {
  window.addEventListener('load', () => {Spotify.getAccessToken()});
}
1 Like

Add this line in Spotify.js
accessToken = accessToken.replace(’=’, ‘’);

In if (accessTokenMatch && expiresInMatch)
after the line accessToken = accessTokenMatch[1];

Try it, hope it helpful.