I am working with React, Express, PostgreSQL, Node, and the Fetch API. When I try to run a "GET" request (within a try block) to get data from my database, the request fails (and enters the catch block) with the following error:
Unexpected token < in JSON at position 0
Here is the failing code that I have on the front end:
const getRequests = async () => {
try {
const responseInfo = await fetch("/api/advice-requests", {
headers: { "Accept": "application/json" },
});
if (responseInfo.status === 200) {
console.log("200 running"); // This is logged to the console.
const data = await responseInfo.json();
console.log("data :", data); // This is NOT logged to the console. It fails.
setAdviceRequests(data.requests);
setResponses(data.responses);
return;
}
} catch (error_o) {
// The UI is updated with the text of the below error
setError(
"Something went wrong on our end. We are looking into how we can improve..."
);
return;
}
};
Here is some of my server code (there is more, but it is not relevant), including some changes I made that worked to solve this problem for other people.
const adviceRequests = require("./controllers/adviceRequests");
const express = require("express");
const app = express();
const cors = require("cors");
app.use(express.json());
app.use(cors());
app.options("*", cors());
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.get("/api/advice-requests", adviceRequests.getAll);
app.listen(PORT, () => {
console.log(`SERVER RUNNING ON ${PORT}.`);
});
Lastly, here is the adviceRequests.getAll function:
getAll: async (req, res) => {
const db = req.app.get("db");
try {
let requests = await db.requests.getAll(req.session.user.id);
let responses = await db.responses.getAll([req.session.user.id]);
return res.status(200).send([requests, responses]);
} catch (error) {
return res.status(500).send({
message:
"Something went wrong on our end. We are looking into how we can improve.",
error,
});
}
},
A bit more information:
The code works just fine when I run it locally
Even on the live server, I can successfully run several POST requests for authentication and adding requests. I just cannot get them.
I have done quite a bit of research into this and am posting my own as a last resort. Nothing that has worked for other people has worked for me so far.
Everytime I have had this "Unexpected token < in JSON at position 0" it was because i was trying to parse an html plain response as if it was a json. Note that every html file starts with a <.
I suggest you change this console.log("200 running"); with a console.log(responseInfo);, that way you'll notice if the response is a json or not.
From what I see, the problem might be the order in which the app.get are defined. Note that express serves first come first served, so since you have already defined an app.get("/*"), everything will be served by that route. Also note that you are sending back an index.html, which matches the issue shown in the frontend.
Related
I created a simple API using express, and deployed it to Heroku, this is the code for it:
const express = require("express");
const app = express();
const cors = require("cors");
app.use(express.json());
app.use(cors());
app.use(express.static("build"));
let notes = [
{
id: 1,
content: "HTML is easy",
date: "2022-05-30T17:30:31.098Z",
important: true,
},
{
id: 2,
content: "Browser can execute only Javascript",
date: "2022-05-30T18:39:34.091Z",
important: false,
},
{
id: 3,
content: "GET and POST are the most important methods of HTTP protocol",
date: "2022-05-30T19:20:14.298Z",
important: true,
},
];
const generateId = (arr) => {
const maxId = arr.length < 0 ? 0 : Math.max(...arr.map((item) => item.id));
return maxId + 1;
};
app.get("/", (req, res) => {
res.send(`<h1>Hello World!</h1>`);
});
app.get("/api/notes", (req, res) => {
res.json(notes);
});
app.get("/api/notes/:id", (req, res) => {
const id = Number(req.params.id);
const note = notes.find((note) => note.id === id);
if (note) {
res.json(note);
} else {
res.status(404).end();
}
});
app.delete("/api/notes/:id", (req, res) => {
const { id } = Number(req.params);
notes = notes.filter((note) => note.id !== id);
res.status(204).end();
});
app.post("/api/notes", (req, res) => {
const body = req.body;
if (!body.content) {
return res.status(400).json({
error: "Content Missing",
});
}
const note = {
content: body.content,
important: body.important || false,
date: new Date(),
id: generateId(notes),
};
notes = notes.concat(note);
res.json(note);
});
app.put("/api/notes/:id", (req, res) => {
const newNote = req.body;
notes = notes.map((note) => (note.id !== newNote.id ? note : newNote));
res.json(newNote);
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
as you can see, the data served to the frontend (A React app) comes from the '/api/notes' endpoint, this endpoint returns a response with the notes array.
After deploying to Heroku (https://fierce-chamber-07494.herokuapp.com/) the functionality of adding notes, and setting importance all work perfectly normal, but what I wasn't expecting was for the data to be persistent even after refreshing the page, visiting it in another device, etc. The data only comes from a variable, not a database, nothing. So why is it persistent? does Heroku modify the variable itself? how does this work?
The top-level code of an Express server often runs once, when you start up the server. Variables declared at that top level are then persistent if there are any handlers that reference them.
Consider how a client-side page with JavaScript works - the page loads, and then the JavaScript runs. If you keep the tab open for a couple hours and then come back to it, you'll see that variables declared on pageload still exist when you come back. The same sort of thing is happening here, except that the persistent environment is on your server, rather than on a client's page.
The code that starts up the Express server - that is, your
const express = require("express");
const app = express();
const cors = require("cors");
app.use(express.json());
app.use(cors());
...
and everything below it - doesn't run every time a request is made to the server. Rather, it runs once, when the server starts up, and then when requests are made, request handlers get called - such as the callback inside
app.get("/", (req, res) => {
res.send(`<h1>Hello World!</h1>`);
});
So, the variables declared at the top-level are persistent (even across different requests) because that server environment is persistent.
That said - something to keep in mind with Heroku is that with their free and cheap tiers, if no request is made to your app for a period of time (maybe 30 minutes), Heroku will essentially turn your server off by spinning down the dyno until another request is made, at which point they'll start your server up again, which will run the top-level code again. So while you'll sometimes see a top-level variable that appears to have its mutated values persist over multiple requests, that's not something to count on if your Heroku plan doesn't guarantee 100% uptime for your server.
I'm learning Express and I face an issue which I can't understand.
When I route to /addPerson I expect to log the name: 'Mike', age: 30 to the console. Instead I got nothing logged to the console. What's wrong in my code?
here's the server.js code
const Express = require('express'),
app = Express(),
PORT = process.env.PORT || 5000,
parser = require('body-parser'),
data = []
// initialize the main project folder
app.use(Express.static('public'))
// running the server
app.listen(PORT, () => {
console.log(`Server is running at port ${PORT}`);
})
// include body parser to handle POST requests
app.use(parser.urlencoded({extended: false}))
app.use(parser.json())
// setup CORS
const cors = require('cors')
app.use(cors())
// GET request
app.get('/', (req, res) => {
res.send('<h1>Home Page</h1>')
})
app.get('/addPerson', (req, res) => {
res.send('<h1>Hello Hany</h1>')
})
// POST request
app.post('/addPerson', (req, res) => {
data.push(req.body)
console.log(data);
})
and here is the client side app.js code
const postData = async ( url = '', data = {})=>{
console.log(data);
const response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
// Body data type must match "Content-Type" header
body: JSON.stringify(data),
});
try {
const newData = await response.json();
console.log(newData);
return newData;
}catch(error) {
console.log("error", error);
}
}
postData('/addPerson', {name: 'Mike', age: 30});
this the files structure
Alright, I've taken a look at your code and this is what I've noticed. Within your server.js file you have this code block:
app.get('/addPerson', (req, res) => {
res.send('<h1>Hello Hany</h1>')
})
That is sending back a static H1 tag when the user creates a get request to localhost:5000/addPerson. Then, directly below that you have your post route but you're never fully accessing that from anywhere (I looked through all your app.js code to double check).
Instead, I have changed your code to return a static html file with a button that allows you to call this function (just as an example so you can see that your routes do in fact work). This isn't the cleanest solution to your problem but I just wanted to make sure you see where the problem lies as I've been in your shoes before when I first started working with express. You can take a look at the CodeSandbox I setup below to replicate your issue and take a look through all the code to get an understanding.
To properly solve your issue using the app.js file you would have to serve the javscript file as your "frontend". Personally I'm a big fan of React so I usually serve my frontend with React, while my backend is express. You can also very easily serve this file using NodeJS in a similar fashion that you are with your "backend". If you were to take the React approach you would be able to modify this code:
app.get("/addPerson", (req, res) => {
res.sendFile(path.resolve(__dirname, "public", "index.html"));
});
To find the frontend section you desire using React (I can recommend react-router if you require multiple routes but I don't want to overwhelm you with too much information yet) and complete the same function. If you have any questions feel free to reach out and let me know! Hopefully this helps!
I have a React web application which currently does fetch calls client-side to update a dashboard with live information (let's say current weather, as an example), meaning that with an increase in users it will cause unnecessary traffic calls and could potentially crash this weather website.
What I am trying to understand is how can I make those fetch calls be server-side? I have looked into creating a Node.js Express server, but I am unsure if it has the functionality to make fetch calls to a remote host.
Here is my code with request-weather which does not really work, unfortunately.
const { response } = require('express');
const express = require('express');
const app = express();
var fetch = require('node-fetch');
const port = process.env.PORT || 5000;
app.use(express.json());
// This displays message that the server running and listening to specified port
app.listen(port, () => console.log(`Listening on port ${port}`));
// create a GET route
app.get('/request-info', (req, res) => {
res.send({ information: 'information call successful' });
});
app.get('/request-weather', (req, res) => {
fetch('http://thisotherwebsite.com/weather-query-that-returns-json',
{method: 'GET',
headers: {' Accept': 'application/json'}})
.then(res => {
return res;
})
});
Couple things:
Your /request-weather handler makes the request to thisotherwebsite but doesn't do anything with the response.
Your .then(res => { return res; }) doesn't actually do anything. You're just taking what fetch already returns and returning it.
If you want to send the response back to the browser you might do something like this:
fetch(...) // make the request
.then(result => result.json()) // extract the data
.then(data => {
res.json(data); // send it to the browser
})
If you want to do additional processing you could await the fetch call and then do whatever else you need to do with it:
app.get('/request-weather', async (req, res) => { // make handler async
// get data from the other site
const data = await fetch(...)
.then(response => response.json());
// package it up with some other stuff
responseData = {
fromOtherSite: data,
myExpressStuff: {
foo: 1,
bar: 2,
}
}
// return it to the browser
res.json(responseData);
Reference:
fetch: response.json() - Extracting data from a fetch response
express response.json() - Sending json to the response (usually to the browser)
I'm new react and nodejs and full stack development in general. I'm trying to make a login page in nodejs/react/mysql. Right now, I'm just trying to do get request for the main login page. I think I'm struggling with connecting the front and backend and the moment.
Nodejs app.js:
const express = require('express');
const bodyparser = require('body-parser');
const cors = require('cors');
const app = express();
const mysql = require('mysql');
let connection = mysql.createConnection({
//Connection is encrypted for security reasons.
host: '***********',
user: '***********t',
password: '***********',
database: '***********'
});
connection.connect(function(err) {
if (err) {
return console.error('error: ' + err.message);
}
console.log('Connected to the MySQL server.');
});
app.listen(3001, () => { console.log('running on port 3001'); });
app.use(cors());
app.use(express.json()); a
app.use(bodyparser.urlencoded({extended: true}));
app.get('/', (req, res) => { res.send('respond with a resource'); });
componentDidMount() react code/fetch request:
componentDidMount() {
// GET request using fetch with error handling
fetch('/')
.then(async response => {
const data = await response.text();
// console.log(data);
console.log('test',data);
// check for error response
if (!response.ok) {
// get error message from body or default to response statusText
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
this.setState({ totalReactPackages: data.total })
})
.catch(error => {
this.setState({ errorMessage: error.toString() });
console.error('There was an error!', error);
});
}
My sql connection works fine, I think it's an issues connecting the front end and backend. Changing following line:.
const data = await response.text();
to:
const data = await response.json();
Returns the following error:
There was an error! SyntaxError: Unexpected token < in JSON at position 0
This is the html it returns in the following screenshot:
Once I figure out how to connect the front end and backend the rest of the project should be easyish to do. The react UI work and my SQl connection works fine. Any help would be greatly appreciated.
You're currently fetching data from the primary site, not the Node.js site you've created.
You should change the fetch to:
fetch('http://localhost:3001')
Additional information
The response you are sending from the backend isn't JSON:
res.send('respond with a resource');
To send JSON, you should use:
res.json({ message: "respond with a resource" });
Then you'll be able to use:
const data = await response.json();
And access the data via:
const message = data.message;
For me it worked just by appending "Https://" to the beginning of the API url.
fetch(`https://api.openweathermap.org/data/2.5/weather?q=${CITY_NAME}&appid=${API_KEY}`)
It took me days and 10s of articles/SO threads to figure out that when doing dev on your local machine the fetch request gets routed to local html document.
I have a React JS application that as a Backend has an API REST made with Node JS.
Currently, my objective is to be able to download files that are on the server.
The correct behavior should be that the user, after clicking on "Download file", should receive the file (Download with browser).
On the server-side, I have something like this (obviously, I'm gonna simplify it by removing JWT middleware, DB queries, etc..):
const express = require('express');
const router = express.Router();
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
app.use(cors({ origin: "http://localhost:3000" }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
router.get('/download', (req, res, next) => {
res.download("\\\\folder\\subfolder\\myfile.txt");
});
app.use('/api', router);
const PORT = 3001;
app.listen(PORT, function() {
console.log("Server is running on port "+PORT);
});
Then, as I have said, I have a React JS application working as a Frontend:
apihelperdownload () {
return axios.get(API_URL + "download").then(function (response) {
return response;
})
}
.............
function downloadFile() {
apihelperdownload().then(
(res) => {
// Should I do something here with response?
},
(error) => {
}
)
}
<button className="download" onClick={() => downloadFile()}>
Download File
</button>
I have two files on my server, one TXT and one JPG.
Both have the correct path (I'm not getting any "file/path not found" error) and I am receiving a "200 status OK" in both cases... But I can't download the files.
Also:
In the JPG case, in Network Tab, on preview sub-Tab I can see the image (so the browser is receiving the image).
And the response looks like this:
(ignore the params and the different url, it's just that here is not simplified)
- In the TXT case, in Network Tab, on preview sub-Tab I can just see a white page.
And the response looks like this:
As you can see, in this second case (.txt file), the data is "empty" ( "" )
Data is the correct text.. I didn't save the txt file.. So it was empty..
I have checked several related questions like this Download a file from NodeJS Server using Express
But unfortunately, I haven't found how to solve my issue.
1) What am I doing wrong on the server-side?
2) What I have to do with the response on client-side?
Thanks
I have found how to solve it without third-party libraries and in quite an "easy way".
First of all, I have changed the request to POST (since I just made GET because I thought it was the only way).
After that, on the Axios request, we have to indicate the responseType as blob:
function apihelperdownload () {
return axios.post(API_URL + "download",{ key: 'value', headers: authHeader(), responseType: 'blob' }).then(function (response) {
return response;
})
}
Then, when we receive the response, we have to create an URL object as a Blob and a link element to download it.
function downloadFile(filename) {
apihelperdownload().then(
(res) => {
const url = window.URL.createObjectURL(new Blob([res.data]));
const link = document.createElement('a');
link.href = url;
if (typeof window.navigator.msSaveBlob === 'function') {
window.navigator.msSaveBlob(
res.data,
filename
);
} else {
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
}
},
(error) => {
alert("Something went wrong");
}
)
}
With this, we can download almost any kind of file very easily.
You can use js-file-download module.
const FileDownload = require('js-file-download');
Axios.get(API_URL + "download")
.then((response) => {
FileDownload(response.data, 'file.txt');
});
Check this response for more: https://stackoverflow.com/a/41940307/6512445