Jamming Get 400 error when searching for artist

Hello,

I have been working through the Jamming project that is apart of the web Dev path. I got through the project and all seemed to be going well till I go and search for an artist. When I search I see the below error in the console.

I have tried several things to fix this for the past few hours but not sure what I did wrong.

Here is my code for Spotify.js

const clientId = '********************************';
const redirectUri = 'http://localhost:3000/';
let accessToken;

const Spotify = {
    getAccessToken(){
        if(accessToken){
            return accessToken;
        }

        //check for access token match 
        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]);

            // this clears the Parameters, allowing us to grab a new access token when it expires.

            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,
                artist: track.artist[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 =>  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 => response.json()
            ).then(jsonResponse => {
                const playlistId = jsonResponse.id;
                return fetch(`https://api.spotify.com/v1/users/${userId}/playlist/${playlistId}/tracks`,{
                    headers: headers,
                    method: 'POST',
                    body: JSON.stringify({uris:  trackUris })

                });
            });
        });
    }
}

export default Spotify;

Here is my Code for 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} 
          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;

Here is my code for 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);
    }

    search(){
        this.props.onSearch(this.state.term);
    }

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

    render(){
        return(
            <div className="SearchBar">
                <input placeholder="Enter A Song, Album, or Artist" />
                <button className="SearchButton" onClick={this.search}>SEARCH</button>
            </div>
        )
    }
}

export default SearchBar;

Here is my Code for playlist.js

import React from 'react';
import './playlist.css'
import Tracklist from '../TrackList/tracklist';

class Playlist extends React.Component{
    constructor(props){
        super(props);
        this.handleNameChange = this.handleNameChange.bind(this);
    }

handleNameChange(event){
    this.props.onNameChange(event.target.value);
}
    
    render(){
        return (
            <div className="Playlist">
                <input defaultValue={'New Playlist'} onChange={this.handleNameChange}/>
                <Tracklist track={this.props.playlistTracks} 
                onRemove={this.props.onRemove}
                isRemoval={true} />
                <button className="Playlist-save" onClick={this.props.onSave}>SAVE TO SPOTIFY</button>
            </div>
        )
    }
}

export default Playlist;

here is my code for SearchResults.js

import React from 'react';
import Tracklist from '../TrackList/tracklist';
import './SearchResults.css';


class SearchResults extends React.Component{
    render(){
        return (
            <div className="SearchResults">
                <h2>Results</h2>
                <Tracklist track={this.props.searchResults} 
                onAdd={this.props.onAdd}
                isRemoval={false}/>
            </div>
        )
    }
}

export default SearchResults;

Here is my code for track.js

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

class Track extends React.Component{
    constructor(props){
        super(props);

        this.addTrack=this.addTrack.bind(this);
        this.removeTrack=this.removeTrack.bind(this);
    }
    renderAction(){
        if(this.props.isRemoval){
            return <button className='Track-action' onClick={this.removeTrack}>-</button>
        } else {
            return <button className='Track-action' onClick={this.addTrack}>+</button>
        }
    }

addTrack(){
    this.props.onAdd(this.props.track);
}

removeTrack(){
    this.props.onRemove(this.props.track);
}

    render(){
        return(
            <div className="Track">
                <div className="Track-information">
                    <h3>{this.props.track.name}</h3>
                    <p> {this.props.track.artist} | {this.props.track.album} </p>
                </div>
                   {this.renderAction()}
            </div>
        )
    }
}

export default Track;

Any additional help is greatly appreciated.

Hello,

If you look closely at the URL in the error message, it’s missing your search term, so Spotify sends back an error response. Assuming you didn’t search with an empty string on purpose, you should trace where that data is supposed to come from and figure out why it isn’t working.

Click for a hint

In your Spotify.search() method, you can add a log of term. Was it your search term? If it was, investigate further into the code. If it isn’t the term you searched for, then work your way up to what calls Spotify.search().

Click for another hint

In your App.js’s search() method, you can log the term before calling Spotify.search(). Was it the value you were expecting?

Click for another hint

Since the term wasn’t the value you expected there either, you need to examine what calls this method. That method gets passed to SearchBar and is used by that component in the search() method there. That search() method uses the value of this.state.term as an argument to the method passed to it from App. Log the value of this.state.term in there. Is it the value you’re expecting?

Click for another hint

The value still wasn’t what was expected. So now you need to think about how that value is updated. The handleTermChange() method is responsible for updating the term state for the component. Go ahead and log the value of event.target.value since that’s the value it uses to set the state. Is it the value you were expecting?

Click for another hint

Not the value you were expecting because there wasn’t a log. That means handleTermChange() wasn’t called at all. Now you need to think about what is supposed to be calling this handler method.

Click for final hint of the debug journey

Now you’re at the point where you examine the JSX for where the handler was added and find that the onChange event for <input /> was never added. So the user can type in the box, but it doesn’t become part of state.

As you get used to debugging, a lot of this stuff will come naturally and you’ll rapidly do these kind of tracing steps with or without logging. Following the data is important.

Now that you have that part working, you’re going to quickly run into your next error. I won’t take you on a debugging journey like the last hint, but I will say that logging the JSON and double checking the exact name of the fields you’re pulling data from will be helpful.

1 Like

That is exactly what I needed to get me over the hump thanks so much for the help. I actually began by calling handleTermChange but received an error. Then when I removed that call it no longer errored out so I assumed it was fixed. When in reality I was just avoiding my initial issue altogether. Thanks again for the help in getting this fixed. With a couple of other minor changes, I had to make to get my playlist to save to spotify everything seems to be working now. Thanks Again

All the Best,
Jake

1 Like