i'm really new to Backend and was trying to fiddle around with API-Calls and Client-Server stuff.
const express = require("express");
const cors = require("cors");
const fetch = require("node-fetch");
const app = express();
app.use(cors());
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));
app.get("/express_backend", (req, res) => {
fetch(
"http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=XXXXXXXXXXXXXXXXXX&steamid=76561197996613749&format=json"
)
.then((response) => response.json())
.then((data) => console.log(data))
.then((data) => res.send(data));
});
so this is my server file and it properly fetches the data from the steam api output screenshot
But im struggling to get this from the server to my client.
import React, { useState, useEffect } from "react";
const App = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch("/express_backend")
.then(function (response) {
return response.json();
})
.then(function (json) {
let fetchedData = json;
setData(fetchedData);
})
.catch(function (err) {
console.log(err.message);
});
}, []);
return (
<div>
<p>{data}</p>
</div>
);
};
export default App;
I think that the "Unexpected end of JSON input" Error is related to the "response.json() line but im not really sure how to go on with this or how to fix it. Thanks in Advance!
there is a small issue on the backend server. In the below Promise chain, the second thenable is not returning anything to the third thenable, so basically you are not writing anything to the 'response' when doing res.send(data)
app.get("/express_backend", (req, res) => {
fetch(
"http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=XXXXXXXXXXXXXXXXXX&steamid=76561197996613749&format=json"
)
.then((response) => response.json())
.then((data) => console.log(data))
.then((data) => res.send(data));
});
The correct order would be to merge second and third then handlers, like so
app.get("/express_backend", (req, res) => {
fetch(
"http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=XXXXXXXXXXXXXXXXXX&steamid=76561197996613749&format=json"
)
.then((response) => response.json())
.then((data) => {
console.log(data);
res.send(data);
})
})
Related
Client/App.js:
import React, { useState, useEffect } from "react";
import Axios from "axios";
const App = () => {
const [movieName, setmovieName] = useState("");
const [movieReview, setmovieReview] = useState("");
const [getReview, setgetReview] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/api/get", (result) => {
console.log(result.data);
setgetReview(result.data);
});
}, []);
const submitReview = () => {
Axios.post("http://localhost:3001/api/insert", {
movieName: movieName,
movieReview: movieReview
})
.then(() => {
alert("Success");
})
.catch((e) => alert(e));
};
return (
<div className="index">
<h2>movie name</h2>{" "}
<input type="text" onChange={(e) => setmovieName(e.target.value)} />
<h2>movie rating</h2>{" "}
<input type="text" onChange={(e) => setmovieReview(e.target.value)} />
<button onClick={submitReview}>submit</button>
{getReview.map((val) => {
return (
<h1>
Movie name : {val.movieName} Movie review: {val.movieReview}
</h1>
);
})}
</div>
);
};
export default App;
Server/index.js:
const express = require("express");
const mysql = require("mysql");
const cors = require("cors");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(express.json());
const db = mysql.createConnection({
host: "localhost",
root: "root",
password: "",
database: "crudatabase"
});
db.connect((err) => {
if (err) throw err;
});
app.get("/api/get", (req, res) => {
const selectStmt = "SELECT movieName,movieReview FROM movie_review;";
db.query(selectStmt, (err, result) => {
res.send(result);
});
});
app.post("/api/insert", (req, res) => {
const movieName = req.body.movieName;
const movieReview = req.body.movieReview;
const insertStmt =
"INSERT INTO movie_review (movieName,movieReview) VALUES (?,?);";
db.query(insertStmt, [movieName, movieReview], (err, result) => {
console.log(err);
});
});
app.listen(3001, () => {
console.log("Server running on 3001");
});
In the above react and express code I am able to insert the data in the database but after inserting then() part in client is not working. Also the useEffect is not working. I tried many ways but not able to get the reason. I would be glad if someone can solve me the error and all the dependencies are already installed.
In your useEffect, you're passing a callback to Axios.get - this is not consistent with the Axios API (you even do it correctly in the submitReview function!):
useEffect(() => {
// change to Promise.then() chain
Axios.get("http://localhost:3001/api/get").then((result) => {
console.log(result.data);
setgetReview(result.data);
});
}, []);
Your then() chain is not working because your POST response handler never returns a status or a response! Just like in your GET handler, your POST handler needs to let the client know that a request has been successful. e.g. res.send(/*...*/) or even just res.sendStatus(200).
As you are dealing with the promise and have used the thenable syntax while submitting the values but you are not using it while getting the values. try using the below code and check whether this resolves your problem. One more concise method to deal with promises is to use async/await try to use the below code hopes this resolves your problem.
useEffect(() => {
const getMovies = async () => {
try {
let { data } = await Axios.get("http://localhost:3001/api/get");
console.log(data);
setgetReview(data);
} catch (error) {
console.log(error);
}
};
getMovies();
}, []);
Your useEffect is returning a promise try to use async await or .then on your code.
Try to change it from:
useEffect(() => {
Axios.get("http://localhost:3001/api/get", (result) => {
console.log(result.data);
setgetReview(result.data);
});
}, []);
To:
useEffect(() => {
const get_data = async () => {
try {
const result = await Axios.get("http://localhost:3001/api/get")
console.log(result.data)
setgetReview(result.data)
} catch (e) {
console.log(e)
}
}
get_data()
}, []);
I am new to React and javascript for that matter and am having trouble setting a components state. What I am trying to do is fetch a list of photos from an endpoint of mine. Once I have the photo list I need to map over the photos and call another endpoint to add some other meta data. My goal is to set the state of my component array once so I only render the webpage one time with all of the images.
I believe I need a Promise.All statement to determine when all of the images have returned from a fetch statement inside of a map. However I am not sure if this is the right approach or how it would look.
I will post some of my code below. Any and all recomendations are welcome, Thanks!
server.js
const express = require('express');
const app = express();
var AWS = require('aws-sdk');
var url = require('url');
var http = require('http');
var s3 = new AWS.S3({apiVersion: '2006-03-01'});
var url = require('url');
var http = require('http');
var sizeOf = require('image-size');
var params = {
Bucket: "owenpersonalphotos",
};
const URL = "http://owenpersonalphotos.s3.amazonaws.com/";
app.get('/api/listPhotos', (req,res) => {
console.log("GET /api/listPhotos")
s3.listObjects(params, function (err, data) {
res.json(data.Contents)
});
});
app.get('/api/getPhoto/:key', (req, res) => {
console.log("GET /api/getPhoto/"+req.params.key)
let key = req.params.key;
s3.listObjects(params, function (err, data) {
let imgUrl = URL+key;
options = url.parse(imgUrl);
http.get(options, function (response) {
var chunks = [];
response.on('data', function (chunk) {
chunks.push(chunk);
}).on('end', function() {
var buffer = Buffer.concat(chunks);
res.json(sizeOf(buffer))
});
})
});
});
const port = 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
App.js
class App extends React.Component {
constructor() {
super();
this.loadPhotos = this.loadPhotos.bind(this);
this.state = {
Photos: []
};
}
componentDidMount() {
this.loadPhotos();
}
loadPhotos() {
let p = []
fetch('api/listPhotos')
.then(res => res.json())
.then(data =>
{ Promise.all(data.map(data => fetch("api/getPhoto/" + data.Key)
.then(res => res.json())
.then(getPhotoData => (p = ({
src:"https://owenpersonalphotos.s3.amazonaws.com/"+data.Key,
width: getPhotoData.width,
height : getPhotoData.height,
id: data.ETag
},this.setState({Photos:p}))))))
})
}
render() {
return (
<div className="App">
{this.state.Photos}
</div>
);
}
}
You could try something along the lines of this. Unfortunately I am unable to replicate your exact scenario so it might not work, but here's how you could resolve a Promise.all.
const initialiser = fetch('api/listPhotos')
.then(res => res.json())
.then(data => { // map every photo request to the promise of the fetch
let requests = data.map(data => fetch("api/getPhoto/" + data.Key)
.then(res => res.json())
.then(getPhotoData => ({
src: "https://owenpersonalphotos.s3.amazonaws.com/" + data.Key,
width: getPhotoData.width,
height : getPhotoData.height,
id: data.ETag
)}))
// Promise.all waits until all jobs are resolved
Promise.all(requests)
.then(responses => this.setState({ Photos: responses });
})
})
I would use React Hooks instead for functional components and storing state into an array
import React, { useState, useEffect } from "react";
State set as empty array below:
const [photos, setPhotos] = useState([]);
This following effect will run getPhotos() once on page load
useEffect(() => {
getPhotos();
}, []);
getPhotos from API and map to state photos
const getPhotos = async () => {
//async way
try {
const response = await fetch("YOUR API URL");
let photos = await response.json;
setPhotos(photos);
} catch (err) {
console.log(err);
}
/* with just promises
return fetch("YOUR API URL")
.then(response => response.json())
.then(response => {
console.log(response);
setPhotos(response);
});
*/
and then you can just map it in the return statement:
<div className="photos-container">
{photos.map((p, i) => {
return (
<div className="photo" key={i}>
<img src={photos.imgSrc} alt={photos.name}>
</div>
);
})}
</div>
Hope this helps I know it's not your example exactly
Your fetches were look like a promise hell situation, I will make it a bit clearer like this:
loadPhotos() {
let promises = [];
fetch('api/listPhotos')
.then(res => res.json())
.then(list => {
for (let photo of list) {
const aCall = fetch("api/getPhoto/" + photo.Key).then(res => res.json());
promises.push(aCall);
}
return Promise.all(promises);
}).then(detailedPhotos => {
const Photos = detailedPhotos.map(item => ({
src: "https://owenpersonalphotos.s3.amazonaws.com/" + item.Key,
width: item.width,
height : item.height,
id: item.ETag
}));
this.setState({ Photos });
})
}
render() {
return (
<div className="App">
{this.state.Photos.map(p => <div>{p.id}</div>)}
</div>
);
}
I'm using a react frontend and fetching data from my node server. I feel like my code looks a bit redundant, is there a better way to refactor all this?
App.js
searchStock = async (value) => {
let priceURL = `/stock/${ value }/price`
// fetch price data
fetch(priceURL)
.then(res => {
if (res.ok) {
res.json()
.then( (result) => {
this.setState({
price: result
})
})
}
else {
console.log("Something went wrong...")
}
})
}
server.js
app.get('/stock/:symbol/price', (req, res) => {
const token = 'abcde123'
const symbol = req.params.symbol
const apiURL = `https://sandbox.iexapis.com/stable/stock/${symbol}/price?token=T${token}`
fetch(apiURL)
.then(response => {
console.log(response.status)
if (response.ok) {
response.json().then((data) => {
res.json(data)
});
}
else {
res.sendStatus(response.status)
}
})
.catch(error => {
console.log(error);
});
})
As these two code segments live in different apps (frontend and backend) I don't think there's a pretty way of DRYing this.
Introduce library file with fetching logic
src/helper.js
exports.fetchHelper = (url) => fetch(url)
.then(response => {
if (response.ok) {
return response.json();
} else {
res.sendStatus(response.status)
}
})
.catch(console.error);
and use respectively
app.js
import { fetchHelper } from 'src/helper'; // or whatever else your bundler setup requires
searchStock = async (value) => {
const priceURL = `/stock/${ value }/price`;
await fetchHelper(priceURL).then((result) => {
this.setState({
price: result
})
})
}
server.js
const fetchHelper = require('src/helper').fetchHelper;
app.get('/stock/:symbol/price', (req, res) => {
const token = 'abcde123'
const symbol = req.params.symbol
const apiURL = `https://sandbox.iexapis.com/stable/stock/${symbol}/price?token=T${token}`
fetchHelper(apiURL).then((response) => {
res.json(data);
})
Or something similar...
I have an express server set up like so:
app.get('/', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
fetch('https://api.adzuna.com:443/v1/api/property/gb/search/1?app_id=1cd4129d&app_key=key&where=se16bx&distance=5&category=for-sale')
.then(response => response.json())
.then(data => {
res.send(data) // Prints result from `response.json()` in getRequest
})
.catch(error => console.error(error))
});
I then have a Next.js page like so:
class About extends Component {
state = {postcode: null}
handleOnSubmit = e => {
// calling the api passing the postcode
}
render() {
return(
<form>
<input name="postcode" />
<button onClick={this.handleOnSubmit}></button>
</form>
)
}
}
About.getInitialProps = async function() {
const res = await fetch('http://localhost:3001/');
const data = await res.json();
return {
results: data
}
}
the API call in the Express server has a hard coded postcode in it.
where=se16bx
How can I pass that value in a dynamic way based on what the user will put into the form?
Thanks
Yes you can create dynamic API call, for complete documentation you can see from here (Check /posts API).
In your case, you just need to add slug into your endpoint, then pass it to your fetcher function:
app.get('/:id*', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
fetch(`https://api.adzuna.com:443/v1/api/property/gb/search/1?app_id=1cd4129d&app_key=key&where=${req.params.id}&distance=5&category=for-sale`)
.then(response => response.json())
.then(data => {
res.send(data) // Prints result from `response.json()` in getRequest
})
.catch(error => console.error(error))
});
Ok, so what I am trying to do is do an axios.get() request pull specific data an id specifically, then use that id that I got to put it as a string literal so I can do my second request. I keep getting Info is not defined.
axios
.get(
`https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/bloodstrive?api_key=${api}`
)
.then(response => {
info = response.data.id;
})
.then(
axios.get(
`https://na1.api.riotgames.com/lol/league/v4/entries/by-summoner/${info}?api_key=${api}`
)
)
.then(response => {
summoner = response.data;
return summoner;
});
let getSummonerId = (req, res) => {
res.status(200).send(summoner);
};
module.exports = {
getSummonerId
};
Fix your chaining:
axios
.get(
`https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/bloodstrive?api_key=${api}`
)
.then(response => {
return response.data.id;
})
.then(info => {
return axios.get(
`https://na1.api.riotgames.com/lol/league/v4/entries/by-summoner/${info}?api_key=${api}`
)
})
.then(response => {
summoner = response.data;
return summoner;
});
Personally, I recommend async for tasks such as this. Makes handling things a lot easier with promises:
let fetchSummoner = async() => {
const res = await axios.get(`https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/bloodstrive?api_key=${api}`);
const info = res.data.id;
const res2 = await axios.get(`https://na1.api.riotgames.com/lol/league/v4/entries/by-summoner/${info}?api_key=${api}`);
const summoner = res2.data;
return summoner;
}
In the current code you haven't added a return statement in the 2nd axios request. Failing to this will not fetch and return the 2nd url.
Please try the below code.
axios
.get(
`https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/bloodstrive?api_key=${api}`
)
.then(response => {
return response.data.id;
})
.then(info => {
return axios.get(
`https://na1.api.riotgames.com/lol/league/v4/entries/by-summoner/${info}?api_key=${api}`
)
})
.then(response => {
summoner = response.data;
return summoner;
});