Api Routes On React + Express App Giving 404 On Heroku
Solution 1:
Final results:
Explanation:
So, even when running this locally I was getting 404's - the issue turned out to be how you were starting the app.
You only need to start the server, and not the client. It looks like you were starting the "built-in" server that comes with create-react-app
... so, your server was never actually accepting requests as your front end was running on port 3000 and your backend was running on whatever port you had set in .env
.
Due to how you have axios
sending requests (just using the current URL, which was running on the built in create-react-app
port, not your server port), it was essentially sending requests to the wrong port.
This is something I should have thought of last night, since I remembered seeing your Heroku app was using the development build of React (via the Firefox React extension) - that should have been a red flag.
I have added 2 new npm
scripts: npm run begin
and npm start
(renamed the original npm start
to npm run start:original
. npm run begin
properly builds your front end, and then starts your backend afterwards. This is ultimately what resolved the issue. I also had NODE_ENV=production
while testing locally.
I also removed npm heroku-postbuild
as it is not needed.
Code Changes:
After getting this to work, I noticed there was something wrong with your front end code - a loop was sending the request over and over - I kept seeing the below info logged to the console. So I also resolved that using the code further down (I did not delete any code, I just commented the code out, so that you can see the changes I made).
I don't know where you're using Mongo at but I tested this using Atlas - I had issues talking to the database after deploying to Heroku, so I also had to change how you were connecting to the database in server.js
. You can also view these changes below, or in the GitHub repo..
Let me know if you want me to send a pull request to your repo so you'll have the updated code and won't have to manually change anything.
Lastly, double check your environmental variables inside Heroku - make sure that they are set.
// This kept being logged to the console
...
actually hit the route
actually hit the route
actually hit the route
actually hit the route
actually hit the route
actually hit the route
......
// This kept going on and on and on after I searched
These are the changes I made to fix the request loop:
// App.jsfunctionApp() {
/**
* Separated your state into 2 different variables.
* Your request loop was happening due to how your
* useEffect was configured (specifically the dependency
* array)
*/const [searchTerm, setSearchTerm] = useState();
const [searchResults, setSearchResults] = useState();
/*
const [search, setSearch] = useState({
term: '',
results: []
});
*/useEffect(() => {
Axios.post(`/api/search`, { term: searchTerm /* search.term */ })
.then(books => {
setSearchResults(books.data);
// setSearch({...search, results: books.data})
});
}, [searchTerm]);
return (
<divclassName="app"><Header /><Router><Routeexactpath='/'><Searchbar /* search={search} <--Noneedforthisparam */ setSearch={setSearchTerm} />
{!searchTerm /* search.term */ ? (
<divclassName="message"><p>Search for a book or whatever</p></div>
) : <SearchListresults={searchResults/*search.results */} />}
</Route><Routepath='/saved'><h2className="title">Saved Books</h2><SavedList /></Route><Footer /></Router></div>
);
}
// Searchbar.jsconstSearchbar = ({/* search, */ setSearch}) => { // <-- No need for search param herereturn (
<formaction="#"method="get"className="searchbar"onSubmit={e => e.preventDefault()}>
<DebounceInputminLength={2}debounceTimeout={300}type="search"placeholder="🔎 search..."onChange={(e) => setSearch(e.target.value)}
/>
</form>
)
}
// server.jsrequire('dotenv').config();
const express = require('express');
const path = require('path');
constAxios = require('axios');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
constPORT = process.env.PORT || 8080;
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/book';
const app = express();
// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(bodyParser.json());
// Serve up static assets (usually on heroku)if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
}
const { Schema } = mongoose;
const bookSchema = newSchema({
info: Schema.Types.Mixed,
});
// *** REMOVED THIS ***// const Book = mongoose.model('Book', bookSchema);// ==========================================================// **********************************************************// CHANGED THE WAY YOU CONNECT TO MONGO// **********************************************************// ==========================================================/** */ mongoose.set('useCreateIndex', true);
/** *//** */const mongoConnection = mongoose.createConnection(mongoUri, {
/** */useUnifiedTopology: true,
/** */useNewUrlParser: true,
/** */useFindAndModify: false,
/** */ });
/** *//** */constBook = mongoConnection.model('Book', bookSchema /*, 'COLLECTION_NAME'*/);
// ==========================================================// **********************************************************// END OF CHANGES// **********************************************************// ==========================================================
app.post('/api/search', (req, res) => {
console.log('actually hit the route');
Axios.get(
`https://www.googleapis.com/books/v1/volumes?q=${req.body.term}`
).then(books => res.json(books.data.items));
});
app.post('/api/save', (req, res) => {
const newBook = newBook({ info: req.body.book });
newBook.save(err => {
if (err) res.json(err);
res.json({ status: true });
});
});
app.post('/api/unsave', (req, res) => {
Book.findByIdAndRemove(req.body.book._id, err => {
if (err) res.json(err);
res.json({ status: true });
});
});
app.get('/api/saved', (req, res) => {
Book.find({}, (err, books) => {
if (err) res.json(err);
else res.json(books);
});
});
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, './client/build/index.html'));
});
/*
const db = mongoose.connection;
db.on('error', // console.error.bind(console, 'connection error:')
error => {
console.log("[MONGOOSE][ERROR]", error);
}
);
db.once('open', function() {
console.log('[MONGOOSE][SUCCESS] Connected to database!');
});
*/
app.listen(PORT, () => {
console.log(`🌎 ==> API server now on port ${PORT}!`);
});
Solution 2:
if you add a console.log in the api save does it run when hitting that url? Even though it returns a 404 it might still be running possibly.
Post a Comment for "Api Routes On React + Express App Giving 404 On Heroku"