Problem using fetch in React inside useEffect - javascript

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

Related

Next.js and Express.js give CORS error, API queries only work at build time

I have a project where I use Next.js on the front-end and Express.js on the back.
Front-end side
The 'pages' file contains 'index.js'. In it, I am sending the following request.
import Home from "./home/home";
import axios from "axios";
import { useState } from "react";
export default function Index({ data }) {
const [products, setProducts] = useState(data);
const [start, setStart] = useState(1);
const getMoreProducts = async () => {
setStart(start + 1);
const { newProducts } = await axios.get(
`${process.env.NEXT_PUBLIC_BACK_END}/test`
);
setProducts([...products, ...newProducts]);
};
return (
<div>
<Home data={products} />
<button onClick={getMoreProducts}> Load more {start}</button>
</div>
);
}
export async function getServerSideProps(context) {
// Fetch data from external API
const { data } = await axios.get(
`${process.env.NEXT_PUBLIC_BACK_END}/productlist/pagination`,
{
params: {
page: 1,
limit: 5,
},
}
);
return {
props: {
data: data || {},
},
};
// Pass data to the page via props
}
Back-end side
const express = require("express");
var cors = require('cors')
const app = express();
require("dotenv").config();
const mongoose = require("mongoose");
mongoose.connect(
"*********",
{ useNewUrlParser: true }
);
const db = mongoose.connection;
db.on("error", (err) => console.error(err));
db.once("open", () => console.log("Connected to Database "));
app.use(express.json());
const productlistRouter = require("./routes/productlist");
const test = require("./routes/test");
app.use("/productlist", productlistRouter);
app.use("/test", test);
app.use(cors())
app.listen(3000, () => console.log("Server is running"));
And here is my Route code :
const express = require("express");
const router = express.Router();
const Product = require("../models/product");
const cors = require("cors");
const corsOptions = {
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "*" },
// ...
],
origin: "*",
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
};
router.get("/showall", async (req, res) => {
try {
const product = await Product.find();
res.json(product);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
router.get("/pagination", cors(corsOptions), async (req, res) => {
const page = req.query.page;
const limit = req.query.limit;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
const products = await Product.find();
const result = products.slice(startIndex, endIndex);
res.json(result);
});
So, When the page is first built with Next.js, the api works, but when I click the 'Load more' button, it gives a CORS error. When I use the same query with 'postman' and other tools, it does not give any error.
On the Next.js side, it works when I send a query to another 3rd party API., but it doesn't work when I send it to my own back-end. And no matter what page or component I do this in, only the APIs that are created at the build time are working.
What could be the reason for this? and how can i solve it? I've read and searched a few articles about cors, but I still haven't found a solution for days.
CORS should be placed on top level as javascript is executed one by one line. Place app.use(cors()) just above the line of app.use("/productlist", productlistRouter);

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.

Pass data from Node server to React from 3rd party API

I am trying to make a request to 3rd party API, and then show it to the user on the website. I am using Gatsby and Node.js. But I have some problem passing data from server to client.
This code is responsible for making a request to the server :
import React from "react"
import axios from "axios"
const Test = () => {
const getDataFromServer = async () => {
try {
const resp = await axios.get("http://localhost:8080/data")
console.log("From frontend:" + resp.data)
} catch (err) {
console.log(err)
}
}
return (
<div>
<button onClick={getDataFromServer}>Get Data</button>
</div>
)
}
export default Test
And here code responsible to make request to 3rd party API from Node:
require("dotenv").config()
const morgan = require("morgan")
const axios = require("axios")
const express = require("express")
const cors = require("cors")
const dataRoutes = require("./routes/dataRoute")
const app = express()
app.use(cors())
app.use("/api", dataRoutes)
app.use(morgan("dev"))
const api = `${process.env.GATSBY_API_URL}?key=${process.env.GATSBY_API_KEY}&scope=jobPost&fields=name,ingress,startDate,endDate,logo`
const axiosInstance = axios.create({
baseURL: api,
})
app.get("/data", async (req, res, next) => {
try {
const response = await axiosInstance.get("/<path>")
return response.data
} catch (error) {
console.log(error)
}
})
app.listen(8080, () => {
console.log("server is listening on port 8080")
})
I retrieve data from API but when I want to pass it to a client then nothing happening.
Thank you in advance! :)

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

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)
}
})

Socket connection in a API route controller retains the data of previous call

I have an API endpoint in my Node/Express app. The endpoint is responsible to upload files. There are several stages involved in the upload process. like image conversion, sending images to another third party API, etc. I am using socket.io to tell the client about the current stage of upload.
The problem is, The socket connection works fine in the first call, but in my second call to the endpoint, the socket connection runs twice and retains the data which I sent in the previous call.
Here's my code:
server.js
import ClientsRouter from './api/routes/clients';
import express from 'express';
import http from 'http';
import io from 'socket.io';
const port = process.env.PORT || 3000;
const app = express();
const server = http.Server(app);
const socket = io(server);
app.set('socket', socket);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.set('view engine', 'ejs');
app.use(express.static('dist'))
app.use('/uploads', express.static('uploads'));
app.use('/api/clients', ClientsRouter);
server.listen(port, () => console.log(`Server Listening on ${process.env.URL}`));
api/routes/clients.js
import express from 'express';
import ClientsController from '../controllers/clients';
ClientsRouter.post('/uploadClientData/', clientDataUpload.array('client_data'), ClientsController.uploadClientData);
controllers/clients.js
const uploadClientData = async (req, res) => {
try {
const files = req.files
const clientFolder = req.body.client_folder
const { dbxUser } = req;
const team_member_id = req.body.team_member_id;
const data = req.files.map( i => ({team_member_id, destination: i.destination.substring(1), filename: i.filename, status: 1 } ))
const io = req.app.get("socket");
console.log("Outside Socket", data); //This contains my currently passed data
io.on('connection', async socket => {
console.log("Socket Connection established");
console.log("Inside Socket", data); //But This contains my current data aling with the data that I passed in previous call
await uploadQueue.collection.insertMany(data)
socket.emit('upload stage', { upload_stage: 2, progress: 33 })
await helpers.convertImagesToWebResolution(team_member_id, req.body.dpi, req.body.resolution);
socket.emit('upload stage', { upload_stage: 3, progress: 66 })
await helpers.uploadImagesToDropbox(team_member_id, dbxUser, clientFolder)
socket.emit('upload stage', { upload_stage: 4, progress: 100 })
})
res.status(200).json({message: "Uploaded"});
} catch (error) {
console.log(error)
res.status(500).json({
error
});
}
}
And in my front-end react component
componentDidMount(){
const { currentFolder } = this.props;
this.setState({ client_folder: currentFolder }, () => this.afterFileSelect())
}
componentDidUpdate(prevProps){
const { selectedFiles } = this.props;
if(prevProps.selectedFiles !== selectedFiles){
this.afterFileSelect()
}
}
afterFileSelect = async () => {
const { selectedFiles, setSelectedFiles, currentFolder, user, uploadSettings} = this.props;
let formData = new FormData()
formData.append('client_folder', currentFolder)
formData.append('team_member_id', user.team_member_id)
formData.append('resolution', uploadSettings.resolution.split("x")[0])
formData.append('dpi', uploadSettings.dpi)
for(let selectedFile of selectedFiles){
formData.append('client_data', selectedFile)
}
let uploadResp = uploadSettings.convert_web_res ? await uploadClientData(formData) : await dropboxDirectUpload(formData)
const endpoint = uploadResp.config.url;
const host = endpoint.substring(0, endpoint.indexOf("api"));
const socket = socketIOClient(host);
socket.on("upload stage", data => {
this.setState({upload_stage: data.upload_stage, progress: data.progress})
data.upload_stage === 4 && this.setState({client_folder: ""})
})
}
Also I want to know if this is the correct way to to track upload progress?

Categories

Resources