When I was sending the requests from the React frontend, I tested to see what type of response Axios gave on the Networks Tab for Chrome, Firefox, Postman and it showed that on localhost it's showing that it's application/json type while in production on Heroku it's text/html type. The routes that I set up are returning res.json() so I'm not sure what could be causing the problem.
index.js
const express = require("express");
const dotenv = require("dotenv");
const path = require('path');
// Load env
dotenv.config({ path: './config.env' });
const app = express();
app.use(express.json());
// Routes
app.use('/', require('./routes/routes'));
const port = process.env.PORT || 8080;
if(process.env.NODE_ENV === 'production')
{
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
}
app.listen(port, () => {
console.log(`Listening at port ${port}`);
});
routes.js
const express = require("express");
const router = express.Router();
const mysql = require('mysql2');
const db = mysql.createPool
({
user: `${process.env.USER}`,
host: `${process.env.HOST}`,
password: `${process.env.PASSWORD}`,
database: `${process.env.DATABASE}`,
connectionLimit: 10,
waitForConnections: true,
});
router.get('/user', (req, res) =>
{
try
{
let id = req.query.id;
let admin = 0;
// Check database to see if there's a user with the same ID as the one given to us.
// If not, create a new user
db.query("SELECT * FROM users WHERE id = ?", [id], (err, result) =>
{
if(result.length == 0)
{
db.query("INSERT INTO users (id, admin) VALUES (?, ?)", [id, admin]);
}
return res.status(200).json({"result": result});
});
}
catch (err)
{
console.log(err);
return res.status(400).json({error: "An error occurred with user"});
}
});
router.post('/addMatch', (req, res) =>
{
try
{
let uid = req.body.uid;
let mid = req.body.mid;
db.query("INSERT INTO matches (uid, mid) VALUES (?, ?)", [uid, mid], (err, result) =>
{
return res.status(200).json({"result": result});
});
}
catch (err)
{
console.log(err);
return res.status(400).json({error: "An error occurred with addMatch"});
}
});
router.post('/deleteMatch', (req, res) =>
{
try
{
let uid = req.body.uid;
let mid = req.body.mid;
db.query("DELETE FROM matches WHERE uid = ? AND mid = ?", [uid, mid], (err, result) =>
{
return res.status(200).json(result);
});
}
catch (err)
{
console.log(err);
return res.status(400).json({error: "An error occurred with deleteMatch"});
}
});
router.get('/getMatches', (req, res) =>
{
try
{
let id = req.query.id;
db.query("SELECT * FROM media WHERE id IN (SELECT mid FROM matches WHERE uid = ?);", [id], (err, matches) =>
{
return res.status(200).json(matches);
});
}
catch (err)
{
console.log(err);
return res.status(400).json({error: "An error occurred with getMatches"});
}
});
router.get('/getMovie', (req, res) =>
{
try
{
let id = Math.floor(Math.random() * 50) + 1;
db.query(`SELECT * FROM media WHERE id = ${id}`, (err, result) =>
{
return res.status(200).json(result[0]);
});
}
catch (err)
{
console.log(err);
return res.status(400).json({error: "An error occurred with getMovie"});
}
});
module.exports = router;
Swipe.js
import React from "react";
import MatchResults from "../components/MatchResults";
import Data from '../components/Data';
import { Container, Button } from 'reactstrap';
import { withAuth0 } from "@auth0/auth0-react";
import axios from 'axios';
import '../styles/Swipe.css';
class Swipe extends React.Component
{
constructor(props)
{
super(props);
const { user } = this.props.auth0;
this.state = {
data: {},
uid: user.sub.split('|')[1],
matches: []
};
this.yesButtonApi = this.yesButtonApi.bind(this);
this.noButtonApi = this.noButtonApi.bind(this);
this.getMovie = this.getMovie.bind(this);
this.getMatches = this.getMatches.bind(this);
}
componentDidMount()
{
this.getMovie();
this.getMatches();
}
yesButtonApi()
{
try
{
var body = {
uid: this.state.uid,
mid: this.state.data.id
}
axios.post('/addMatch', body).then(() =>
{
this.getMovie();
this.getMatches();
});
}
catch (error)
{
console.log(error)
}
}
noButtonApi()
{
try
{
this.getMovie();
}
catch (error)
{
console.log(error);
}
}
getMovie()
{
try
{
axios.get('/getMovie').then((response) =>
{
this.setState
({
data: response.data
});
});
}
catch (error)
{
console.log(error);
}
}
getMatches()
{
try
{
let uid = this.state.uid;
axios.get(`/getMatches?id=${uid}`).then((response) =>
{
this.setState
({
matches: response.data
});
});
}
catch (error)
{
console.log(error);
}
}
render()
{
return (
<div className="text-center">
<Container className="swipe">
<Data title={this.state.data.title} description={this.state.data.description} rating={this.state.data.rating} preview={this.state.data.video}/>
<Button color="success" onClick={this.yesButtonApi} className="ml-sm">Yes</Button>
<Button color="danger" onClick={this.noButtonApi} className="ml-sm">No</Button>
<MatchResults matches={this.state.matches} uid={this.state.uid}/>
</Container>
</div>
)
}
}
export default withAuth0(Swipe);
MatchResults.js
import React, { Component } from 'react';
import { Card, CardBody, Container, CardHeader, Row, Col, Collapse, Button } from 'reactstrap';
import { AiFillStar } from 'react-icons/ai';
import '../styles/MatchResults.css';
import axios from 'axios';
class MatchResults extends Component
{
constructor(props)
{
super(props);
this.state = { isOpen: false, matches: this.props.matches, uid: this.props.uid };
this.toggle = this.toggle.bind(this);
this.getMatches = this.getMatches.bind(this);
this.deleteButtonApi = this.deleteButtonApi.bind(this);
}
componentDidUpdate(prevProps)
{
if(this.props !== prevProps)
{
this.setState
({
matches: this.props.matches
});
}
}
toggle()
{
try
{
this.setState
({
isOpen: !this.state.isOpen
});
if(this.state.isOpen)
{
this.getMatches();
}
}
catch (error)
{
console.log(error);
}
}
getMatches()
{
try
{
let uid = this.state.uid;
axios.get(`/getMatches?id=${uid}`).then((response) =>
{
this.setState
({
matches: response.data
});
});
}
catch (error)
{
console.log(error);
}
}
deleteButtonApi(e)
{
try
{
var body = {
uid: this.state.uid,
mid: e.target.value
}
axios.post('/deleteMatch', body).then(() =>
{
this.getMatches();
});
}
catch (error)
{
console.log(error);
}
}
render()
{
return (
<Container className="match-results">
<div className="text-center">
<Button color="primary" onClick={this.toggle} style={{ marginBottom: '1rem' }}>View Matches</Button>
<Collapse isOpen={this.state.isOpen}>
<Row>
{
this.state.matches.map((item, val) =>
{
val += 1;
return (
<Col sm="12" md="6" lg="4">
<Card key={val} className="match-results-card" data-testid="card">
<CardHeader>
<div data-testid="title">
{item.title}
</div>
<span data-testid="rating">
<AiFillStar />
{item.rating}
</span>
</CardHeader>
<CardBody>
<div data-testid="description">
{item.description}
</div>
</CardBody>
<Button className="deleteBtn" color="danger" onClick={this.deleteButtonApi} value={item.id}>Delete</Button>
</Card>
</Col>
)
})
}
</Row>
</Collapse>
</div>
</Container>
)
}
}
export default MatchResults;
Localhost
Localhost Response
Production
Production Response
The response you're getting in your production deployment is the default index.html served when there's a problem with your node server configuration. It is not about the response being text/html, its about the node server being setup incorrectly (possibly none of the routes are working). The underlying problem could be anything, a few examples in this question.
I suggest start by looking at your node.js logs in your production environment (debugging Node.js application on Heroku)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With