Search code examples
node.jsreactjsapiexpressitunes

React and Express iTunes Search API :: Error: Request failed with status code 404


I am currently creating an iTunes search application using an API and am stuck attempting to fetch the data to put together the result component.

Amongst other resources, I have been referring to the following information: iTunes Search API

Please see below my code:

Backend - controllers - index.js

// Requiring Express.
const express = require("express");
// Requiring fetch from Node Fetch.
const fetch = require("node-fetch");

exports.getController =
  ("/search",
  (req, res) => {
    fetch(
      `https://itunes.apple.com/search?term=${req.query.name}&limit=30&media=${req.query.type}`
    )
      .then((res) => res.json())
      .then((results) => {
        res.send(results);
      });
  });

Backend - routes - index.js

// Requiring Express.
const express = require("express");
// Requiring Router from Express.
const router = express.Router();
// Requiring controllers from the controllers folder's index.js.
const { getController } = require("../controllers/index.js");

router.get("/search", getController);

// Exporting controllers to server.js.
module.exports = router;

Backend - server.js

// Requiring Express.
const express = require("express");
// Requiring Morgan.
const morgan = require("morgan");
// Requiring CORS.
const cors = require("cors");
// Initializing express with variable "app".
const app = express();
// Requiring the routes index.js file.
const routes = require("./routes/index.js");
// Requiring Helmet.
const helmet = require("helmet");

/**
 * Enabling App usages.
 */

const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use(express.json());
app.use(morgan("start"));
app.use(cors());
app.use(helmet());

app.use("/", routes);

const path = require("path");
if (process.env.NODE_ENV === "production") {
  app.use(express.static("frontend/build"));
  app.get("*", (req, res) => {
    res.sendFile(path.resolve(__dirname, "frontend", "build", "index.html"));
  });
}

const PORT = process.env.PORT || 8080;
app.listen(PORT);
console.log(
  "Navigate to http://localhost:8080/search. Server is listening on port",
  PORT
);

app.use(function (err, req, res, next) {
  console.log(err.stack);
  res.status(500).send("Something broke!");
});

Frontend - search.js

// Imported react libraries and hooks.
import React, { useState, useEffect } from "react";
import axios from "axios";
// Imported Link from React Router Dom.
import { Link } from "react-router-dom";
// Imported components from React Bootstrap.
import { Row, Dropdown, ButtonGroup, Button } from "react-bootstrap";
// Imported icons from FontAwesome.
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSearch } from "@fortawesome/free-solid-svg-icons";
// Imported components.
import Results from "./results.js";

const Search = () => {
  const [name, setName] = useState("");
  const [type, setType] = useState("");
  const [results, setResults] = useState([]);
  const [favourites, setFavourites] = useState([]);

  const addToFav = (fav) => {
    setFavourites([...favourites, fav]);
  };

  useEffect(() => {
    localStorage.setItem("Favourites", JSON.stringify(favourites));
  }, [favourites]);

  const submitSearch = (e) => {
    e.preventDefault();

    axios
      .get(`/search/${name}/${type}`, {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
        },
      })
      .then((res) => {
        const queryAdded = res.data.results;
        setResults(queryAdded ? queryAdded : []);
      })
      .catch((err) => {
        console.log(err);
      });
  };

  let nameEntry = "";
  const nameSubmit = (e) => {
    const entry = e.target.value;
    nameEntry = entry;
    setName(nameEntry);
  };

  const categories = [
    { name: "MUSIC", value: "music" },
    { name: "MUSIC VIDEO", value: "musicVideo" },
    { name: "APPS", value: "software" },
    { name: "EBOOK", value: "ebook" },
    { name: "AUDIO BOOK", value: "audiobook" },
    { name: "PODCAST", value: "podcast" },
    { name: "MOVIES", value: "movie" },
    { name: "TV SHOW", value: "tvShow" },
    { name: "SHORT FILM", value: "shortFilm" },
  ];

  return (
    <div id="searchcontainer">
      <div id="searchcontent">
        <div id="searchinput">
          <input
            type="text"
            placeholder="Search..."
            name="name"
            onChange={nameSubmit}
          />
          <Link to={`/search`}>
            <button id="searchbutton" type="submit" onClick={submitSearch}>
              <FontAwesomeIcon icon={faSearch} title="Search" />
            </button>
          </Link>
        </div>

        <Dropdown as={ButtonGroup}>
          <Button variant="info" size="sm">
            CATEGORIES
          </Button>
          <Dropdown.Toggle
            split
            variant="info"
            id="dropdown-split-basic"
            drop="bottom"
          />

          <Dropdown.Menu>
            {categories.map((category, i) => (
              <Dropdown.Item
                as="button"
                key={i}
                id="searchcategories"
                value={category.value}
                checked={type === category.value}
                onChange={(e) => setType(e.currentTarget.value)}
              >
                {category.name}
              </Dropdown.Item>
            ))}
          </Dropdown.Menu>
        </Dropdown>
      </div>

      <div className="search-page">
        <div className="container-fluid">
          <Row md={5}>
            {results.length !== 0 ? (
              results.map((content) => (
                <Results addToFav={addToFav} content={content} />
              ))
            ) : (
              <h1></h1>
            )}
          </Row>
        </div>
      </div>
    </div>
  );
};

// Exported search.js to landing.js.
export default Search;

Unfortunately I am getting the following error:

Console Screenshot

I have tried a couple of things, but nothing seems to be working. In fact, I think I might be over-complicating the code with all of my fiddling.

I would appreciate any help that anyone is willing to offer.


Solution

  • There are a few problems :

    • Currently, you're sending requests to the address of React app (http://localhost:3000). You need to send the request to the NodeJS application instead.
     axios
          .get(`http://localhost:8080/search/${name}/${type}`, { // add the address of NodeJS application
    
    • If you want to send data like this /search/${name}/${type}, then in the backend, req.query won't work. You should use req.params and change your route accordingly. Read the Express document here. Or, you need to change the URL in the frontend part to /search/?name={name}&type=${type}
    • Finally, in the log, the URL is http://localhost:3000/search/korn. There is 1 missing parameter, which can give you a bad URL in your backend app. https://itunes.apple.com/search?term=${req.query.name}&limit=30&media=${req.query.type} may result https://itunes.apple.com/search?term=korn&limit=30&media=undefined You need a mechanism to deal with missing data.