Node FetchError: invalid json response body - unexpected token < in JSON - javascript

When I'm trying to get the response from my server route I'm getting this error: FetchError: invalid json response body - unexpected token < in JSON
I think the problem is when I do response.json()?
When I use Postman to reach the same endpoint I got the response that I want. What is happening here?
note: my api tokens can be publicly used
server.js
const express = require('express')
const path = require('path')
const bodyParser = require('body-parser')
const fetch = require('node-fetch')
const app = express()
const port = process.env.PORT || 5000
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
const TRANSLINK_TOKEN = 'j2bXKzENILvyoxlZ399I'
const TRANSLINK_URL = 'http://api.translink.ca/rttiapi/v1/buses?apikey='
// API routes
app.get('/buses/location', (req, res) => {
const apiURL = `${TRANSLINK_URL}${TRANSLINK_TOKEN}`
console.log(apiURL)
fetch(apiURL)
.then(response => {
if (response.ok) {
console.log("response ok")
response.json()
.then((data) => {
res.json(data)
})
}
else {
res.sendStatus(response.status)
}
})
.catch(error => {
console.log(error)
alert(error.message)
})
})
app.listen(port, () => console.log(`Listening on port ${port}`))

You API return XML not json, please make sure that apiURL return valid json object
to check the response please put console.log(data) before res.json(data)
You can use XML2JSON package to convert the response to json format or use res.send(data) to retrieve xml response as is
fetch(apiURL)
.then(response => {
if (response.ok) {
console.log("response ok");
console.log(response.body);
// Add XML2JSON to convert body
res.send(response.body);
}
else {
res.sendStatus(response.status)
}
})

Related

Fetch POST - send JSON to backend

Im stuck at something very trivial trying to build my frontend.
My main.js looks like this :
const textfield = document.getElementById('inpTextField')
const btnSendPost = document.getElementById('btnSendPost')
btnSendPost.addEventListener('click', () => {
sendData()
})
function sendData() {
let txtValue = textfield.value
console.log(txtValue)
let sendingJSON = {
"body" : `${txtValue}`
}
fetch("http://localhost:3000/post", {
method: "post",
body: JSON.stringify(sendingJSON),
headers: { "Content-Type": "application.json"}
})
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.log(err))
}
but my backend (express 4.18) doesnt register POST request, and console gives back enigmatic :
TypeError: Failed to fetch
at <anonymous>:1:876
at sendData (main.js:17:5)
at HTMLButtonElement.<anonymous> (main.js:7:5)
index.js (backend)
const express = require('express')
const app = express()
const port = 3000
const bp = require('body-parser')
let randomJson = {
"apple" : "cider viegar"
}
app.use(bp.json())
app.use(bp.urlencoded(({ extended: true})))
app.get('/',(req,res) => {
res.json(randomJson)
})
app.post('/post', (req, res) => {
var body = req.body
console.log(body)
res.send("ok")
})
app.listen(port, () => {
console.log(`Server running on ${port}`)
})
any ideas ?

How to deal with rate limited API services using React/Node.js? (ReferenceError)

Hi I'm new to React and NodeJS and currently I am trying to work my way around pulling from a rate limited API service. My idea is to have NodeJS pull data from the API service every 30 minutes or so and send that to my express "/youtube" endpoint for my frontend to be able to infinitely pull from. However I don't know if this is the best practice and I am also running into an ReferenceError with my code. Any help would be greatly appreciated.
I'm able to console.log(data) so I know the variable is defined but I am unsure on how to pass it to app.get() properly.
ERROR
ReferenceError: data is not defined
CODE
const express = require('express')
const axios = require('axios')
const app = express()
const url = "**Youtube API URL**"
axios.get(url)
.then((response) => {
let data = response.data.items
console.log("Posted data")
console.log(data)
})
.catch(
err => console.log(err)
)
app.get("/youtube", (req,res) => {
res.send(data)
})
app.listen(5000, () => {console.log("server started on port 5000")})
Data is undefined in your route because it is defined within the callback of your axios get callback. Even if it were to be defined prior to the axios call, it would still be undefined because the api call is an async operation...
A simple solution would be to make the axios call within your route callback, and send the response within the axios callback. Although there are tons of coding techniques/patterns to solve this issue, this helps illustrate your problem:
app.get("/youtube", (req,res) => {
axios.get(url)
.then((response) => {
let data = response.data.items
console.log("Posted data")
console.log(data)
res.send(data)
})
.catch(
err => console.log(err)
)
})
Of course, you could create an async function and call it from within the route:
async function makeCall() {
const data = await axios.get(url)
.then((response) => {
let resdata = response.data.items
return resdata
})
.catch(
err => console.log(err)
)
return data;
}
Then in your route:
app.get("/youtube", async (req,res) => {
const data = await makeCall();
res.send(data);
}
Caching the data from the response to be made readily available is another issue altogether...
Thank you everyone for your insight. I've ended up taking the Caching approach to be able to deal with API rate limit of the service that I'm pulling from. I make a call from the API in the beginning of my server.js file and added a setInterval function to pull from the API every 30 minutes to update the data and then store it in a cache.
I then pull that data from the cache and send it to my "/youtube" express endpoint.
Here's my working code in hope that it helps anyone else that runs across the same issues as I have.
server.js
const axios = require('axios')
const NodeCache = require('node-cache')
const express = require('express')
const cors = require('cors')
const app = express()
app.use(cors())
const port = process.env.PORT || 4000
const url = " **Youtube API url** "
const myCache = new NodeCache();
function initialCall(){
axios.get(url)
.then((response) => {
const result = response.data.items
myCache.set("key", result)
console.log("Retrived from API")
})
.catch(
err => console.log(err)
)
}
initialCall()
setInterval(initialCall, 1800000)
app.get("/", (req,res) => {
const welcome = "Welcome"
res.send(welcome)
})
app.get("/youtube", (req,res) => {
if(myCache.get("key")){
res.send(myCache.get("key"))
console.log("Retrieved from cache")
}
})
app.listen(port, () => {console.log(`server started on port: ${port} `)})

how can i send custom sms message with twilio api via express

so im trying to make a simple function in a web that has input and button , and when i click the button twilio api send message with the body of input value life if input is hello the message sent is hello, this is the index.js file which is include the simple function that gonna send the message and i don't know if i should use POST method or get just look
let input = document.querySelector("input").value;
document.querySelector("button").addEventListener("click", whatTheHell);
let whatTheHell = () => {
fetch("/sendSms")
.then((res) => res.json())
.then((res) => console.log(res))
.catch((err) => console.log(err));
};
and this the express.js file that contain the twilio api that gonna send the sms
const express = require("express");
if (process.env.NODE_ENV !== "production") {
require("dotenv").config();
}
const accountSid = process.env.accountSid;
const authToken = process.env.authToken ;
const app = express();
const client = require("twilio")(accountSid, authToken);
app.use(express.json());
app.use(express.static("public"));
app.get("/sendSms", (req, res) => {
client.messages
.create({
body: "message from me",
messagingServiceSid: "MGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
to: "NUMBER",
})
.then((message) => {
res.json({ message: message }).done();
});
});
app.listen(3000, () => {
console.log("Server Started");
});
so what i want here in body: "message from me" is to be something like this body : user.input or something like that , i tried using post method and did req.body.msg and msg is input.value but it dont accept post method .
Twilio developer evangelist here.
I would recommend making this a POST request. You need to update a few things to get your input from the front end to the server. Let's start with the front end.
Instead of getting the input's value straight away, you should wait until the button is clicked to get the value. Then, when it is clicked, you need to make a POST request with the message you want to send in the body of the request. One way to do that is to JSON stringify an object of data.
let input = document.querySelector("input");
document.querySelector("button").addEventListener("click", whatTheHell);
let whatTheHell = () => {
const message = input.value;
fetch("/sendSms", {
method: "POST",
body: JSON.stringify({ message: message }),
headers: {
'Content-Type': 'application/json'
}
})
.then((res) => res.json())
.then((res) => console.log(res))
.catch((err) => console.log(err));
};
Now, on the server side we need to update your endpoint to receive POST requests. You are already using the express JSON parsing middleware, so the message will be available as req.body.message. We can then use that in the request to Twilio.
const express = require("express");
if (process.env.NODE_ENV !== "production") {
require("dotenv").config();
}
const accountSid = process.env.accountSid;
const authToken = process.env.authToken ;
const app = express();
const client = require("twilio")(accountSid, authToken);
app.use(express.json());
app.use(express.static("public"));
app.post("/sendSms", (req, res) => {
const message = req.body.message;
client.messages
.create({
body: message,
messagingServiceSid: "MGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
to: "NUMBER",
})
.then((message) => {
res.json({ message: message });
})
.catch((error) => {
console.error(error);
res.status(500).json({ error: error.message });
});
});
app.listen(3000, () => {
console.log("Server Started");
});
And that should work.
You can use a query paramater to send the message to your express server and retrieve them in your server as explained here: How to get GET (query string) variables in Express.js on Node.js?.
If you make your method a post method when sending it you also need to make your express from get to post like so: app.get() -> app.post()

Axios NPM not fetching data express.js

I have a express.js route whose code is below: -
const path = require("path");
const express = require("express");
const hbs = require("hbs");
const weather = require("./weather");
const app = express();
app.get("/weather", (req, res) => {
if (!req.query.city) {
return res.send({
error: "City Not Found",
});
}
res.send({
currentTemp: weather.temp,
});
});
And I also have a file to fetch data from api using axios whose code is here
const axios = require("axios");
axios
.get(
"https://api.openweathermap.org/data/2.5/weather?q=samalkha&appid=91645b79f9eac8808153c90472150f2d"
)
.then(function (response) {
module.exports = {
temp: response.data.main.temp
}
})
.catch(function (error) {
console.log("Error Spotter");
});
As I am using res.send I should get a json with currentTemp and the value of current temp should be temperature that I will get from weather.js file but I am getting a blank json array.
Try this.
You'll get the temperature in the localhost:3000
If you want to render the data for EJS (or something) instead of .then((data) => res.json(data.main.temp)) use:
.then((data) => res.render("index", { weather: data })
--
const URL =
"https://api.openweathermap.org/data/2.5/weather?q=samalkha&appid=91645b79f9eac8808153c90472150f2d"
const express = require("express")
const axios = require("axios")
const app = express()
const PORT = 3000
app.get("/", (req, res) => {
axios
.get(URL)
.then((response) => response.data)
.then((data) => res.json(data.main.temp))
.catch((err) => console.log(err))
})
app.listen(PORT, () => {
console.log(`Listening at http://localhost:${PORT}`)
})
module.exports is processed when the module is being defined. when you import weather, it does not exists therefore you get no data.
try to export a function which does the request and add a callback function as argument so you can pass the request result to it.
Change your weather.js to the following example:
const axios = require("axios");
let temperature;
async function getTemp() {
await axios
.get("https://api.openweathermap.org/data/2.5/weather?q=samalkha&appid=91645b79f9eac8808153c90472150f2d")
.then(function (response) {
temperature = response.data.main.temp
})
.catch(function (error) {
console.log("Error Spotter");
});
}
module.exports = {
temp: getTemp
}
This will actually return the fetched temperature.

Problem using fetch in React inside useEffect

I'm new to node and react and I am trying to fetch some data and show it on my react page. It's pretty simple. I have an express server running on localhost:3001 and my react app is on localhost:3000.
I'm attempting to fetch data and then set that data to a state via a hook. I can't seem to get the data on the react page or in the web developer console. Is there a way I can see the data that is being fetched in the console?
Here is my React component:
import React, { useState } from "react";
function App() {
const [weatherData, setWeatherData] = useState("");
console.log(weatherData);
React.useEffect(() => {
const fetchData = async () => {
const result = await fetch(
"http://localhost:3001"
);
const data = await result.json();
console.log("data", data);
setWeatherData(data);
};
fetchData();
})
return (
<div>
<h1>The temprature is {weatherData}</h1>
</div>
);
}
export default App;
Here is my node server:
const express = require('express');
const bodyParser = require('body-parser');
const https = require("https");
const cors = require('cors');
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'jsx')
app.use(express.static("public"));
app.get("/", function (req, res) {
const query = Chicago;
const apiKey = "a valid key";
const unit = "imperial";
const url = "https://api.openweathermap.org/data/2.5/weather?appid=" + apiKey + "&q=" + query + "&units=" + unit;
https.get(url, (response) => {
console.log("statusCode", res.statusCode);
response.on("data", (d) => {
const weatherData = (JSON.parse(d));
console.log(weatherData);
res.send(weatherData);
});
}).on("error", (e) => {
console.error(e);
})
});
const port = process.env.PORT || 3001;
app.listen(port, () => console.log(`Listening on port ${port}`));
The result I get is no data and these 2 errors in chrome dev tools console.
index.js:1 Uncaught SyntaxError: Unexpected token '<'
App.jsx:19 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
Any help is greatly appreciated!!
You need to specify the Content-Type of data returned from the server.
Either you can use res.setHeader before sending response or res.json() to send json response
https.get(url, (response) => {
console.log("statusCode", res.statusCode);
response.on("data", (d) => {
const weatherData = JSON.parse(d);
console.log(weatherData);
res.json(weatherData); // use json response
});
}).on("error", (e) => {
console.error(e);
})
It seems there maybe an error occur in result.json().
Probably the request return html rather then json.
You can use postman or other tools to get the real response by 'localhost:3001' to clear where goes wrong.
const data = await result.json();
this line will occur a problem if result does not have valid json.
that's why you have encountered an error. catch that error and see the response

Categories

Resources