Unable to display API data on React frontend - javascript

I'm trying to return data fetched from a private API and display it on a page. My frontend use React JS and my backend use node with Express and Axion. My code work up to the point of returning the data. I get my APi Key and fetch my data but the data is not transferred to my page (Quotes.js).
Backend
app.js
import express from "express";
import { getCase } from "./getCase.js";
const app = express();
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.get("/", function (req, res) {
console.log("app.js call getCase");
res.send(getCase());
//console.log(req);
});
//console.log(Quote.getQuote());
let port = process.env.PORT;
if (port == null || port == "") {
port = 5000;
}
app.listen(port, function () {
console.log(`Server started on port ${port}...`);
});
Backend getCase
import { getToken } from "./nsApiToken.js";
import axios from "axios";
let getData = "";
console.log("begin of getCase");
const getCase = async () => {
let tokenRes = await getToken();
const url =
"https://5156735-sb1.app.netsuite.com/app/site/hosting/restlet.nl?script=860&deploy=1&recordtype=supportcase&id=717986";
try {
const res = await axios.get(url, {
headers: {
Authorization: `Bearer ${tokenRes.data.access_token}`,
},
});
return res;
} catch (error) {
return error;
}
};
export { getCase };
Frontend App.js
import logo from "./logo.svg";
import "./App.css";
import Quotes from "./Quotes.js";
function App() {
return (
<div className="App">
<header className="App-header">
<Quotes />
</header>
</div>
);
}
export default App;
Frontend Quotes.js
import React, { useState, useEffect } from "react";
import axios from "axios";
const Quotes = async () => {
const [text, setText] = useState([]);
const [author, setAuthor] = useState("");
const getQuote = await axios
.get("http://localhost:5000", {
crossdomain: true,
})
.then((res) => res.data)
.then((data) => {
setText({
data: data,
});
console.log("res: ", text);
});
return (
<div>
<button onClick={getQuote}>Generate Quote</button>
<h1>{text}</h1>
<h3>{author}</h3>
</div>
);
};
export default Quotes;
Process:
When I run my process the front execute and call Quotes.js in the axios get process.
app.js then route to home ('/') and call getCase via the app.get.
The getCase process execute get the API token and add it in the headers Authorization. The process initiate the call and fetch the data (if I console.log(res.data.fields.phone) or console.log(res.data.id) I see the correct data.
In my Quotes.js I want to display the data but res.data is empty, yet I get back status 200.
I've been trying to understand why it is not passing the data from the backend to the frontend.

There are several problems and some improvements to be made.
Backend
Problem - You are sending the entire AxiosResponse in the response from your Express app
Just send the data
const getCase = async () =>
(
await axios.get(
"https://5156735-sb1.app.netsuite.com/app/site/hosting/restlet.nl",
{
params: {
script: 860,
deploy: 1,
recordtype: "supportcase",
id: 717986,
},
headers: {
Authorization: `Bearer ${(await getToken()).data.access_token}`,
},
}
)
).data; // Return the data, not the whole response
Problem - getCase() is async
You need to await the result
app.get("/", async (req, res, next) => {
try {
res.json(await getCase());
} catch (err) {
next(err); // send the error to the Express error handler
}
});
Improvement - Creating your own CORS middleware is a waste of time
By the time you create a comprehensive CORS middleware, it will look exactly the same as the standard one so just use that
import express from "express";
import cors from "cors";
const app = express();
express.use(cors());
Frontend
Problem - React function components cannot be async
Function components must return a valid JSX node. Remove async from Quotes
Problem - getQuote should be a function
In order to trigger getQuote by button click, it needs to be a function
// if text is an object, initialise it as one
const [text, setText] = useState({});
const getQuotes = async () => {
try {
// there is no "crossdomain" Axios option
const { data } = await axios.get("http://localhost:5000");
setText({ data });
} catch (err) {
console.error(err.toJSON());
}
};
Problem - the text state is an object
JSX cannot render plain objects, you instead need to reference properties that can be rendered.
<h1>{text.data?.some?.property}</h1>
No idea what your response object looks like so this is just generic advice

The reason why this is not working is for two reasons. Firstly, res.data is not an asynchronous function. And since you are doing await, you can just get data. Secondly, you need to make your API calls and setState in the useEffect hook or else it would just end up in an infinite rerender situation. You just have to do the following and it should work:
useEffect(() => {
const fetchData = async () => {
const {data} = await axios
.get('http://localhost:5000', {
crossdomain: true
})
setText(data)
}
fetchData()
}, [])

Related

React - axios fetching empty array [duplicate]

I am currently working on social media mern stack react app. I am using node js and express as my backend services , also using mongoose to store my data and axios and redux thunk which connect the backend to the front end. Till now I had no issue recieving and sending data to the server. Right now I am trying to create search post get request ,base on a keyword the user entered. The issue with it, that when I am sending the keyword to the server instead of recieving the string it gets undefined value, like redux thunk not sending anything. I will be very thankful if someone could help me with that. I am watching the code over and over again and can't find out the reason for that.
My post controller class(I copied only the relevant function):
import express from "express";
const app = express();
import Post from "../model/PostModel.js";
import ErrorHandlng from "../utilities/ErrorHandling.js";
import bodyParser from "body-parser";
import catchAsync from "../utilities/CatchAsync.js";
import User from "../model/UserModel.js";
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
export const getPosts = catchAsync(async (req, res, next) => {
const data = req.body.keyword;
const page = parseInt(req.query.page || "0");
const PAGE_SIZE = 20;
const query = new RegExp(data, "i");
const total = await Post.countDocuments({});
const posts = await Post.find({ $or: [{ title: query }, { content: query }] })
.limit(PAGE_SIZE)
.skip(PAGE_SIZE * page);
if (!posts) {
return next(new ErrorHandlng("No posts were found", 400));
}
res.status(200).json({
status: "success",
data: {
totalPages: Math.ceil(total / PAGE_SIZE),
posts,
},
});
});
My api class(front end,copied only the calling for that specific get request):
import axios from "axios";
const baseURL = "http://localhost:8000";
axios.defaults.withCredentials = true;
const API = axios.create({
baseURL,
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
export const getPostsByKeyword = (keyword, page) =>
API.get(`/post/getPostsByKey?page=${page}`, keyword);
Post slice class:
export const fetchPostsByKeyWord = createAsyncThunk(
"post/getKeyword",
async ({ keyword, page }, { fulfillWithValue, rejectWithValue }) => {
try {
const response = await api.getPostsByKeyword(keyword, page);
if (response.statusCode === "400") {
throw new Error("There are no available posts");
}
const fetchData = await response.data.data.posts;
const totalPages = await response.data.data.totalPages;
return fulfillWithValue({ fetchData, totalPages });
} catch (err) {
console.log(err.response.message);
}
}
);
const initialState = { status: "undefined" };
const PostSlice = createSlice({
name: "post",
initialState,
reducers: {},
extraReducers: {},
});
export const postActions = PostSlice.actions;
export default PostSlice;
Calling the backend:
dispatch(fetchPostsByKeyWord({ keyword, page }))
.unwrap()
.then((originalPromiseResults) => {
console.log("thte " + " " + originalPromiseResults.totalPages);
console.log("The data is" + originalPromiseResults.fetchData);
setTotalPages(originalPromiseResults.totalPages);
})
.catch((err) => {
console.log(err.message);
});
As you can see I have not copied the whole code, I copied only the parts that are relevants for the question.
Browsers cannot currently send GET requests with a request body. XMLHttpRequest (which Axios uses) will ignore it and fetch() will trigger an error.
See also HTTP GET with request body for extra discussion on why trying this might be a bad idea.
You should instead pass everything required in the query string, preferably via the params option so it is correctly encoded...
export const getPostsByKeyword = (keyword, page) =>
API.get("/post/getPostsByKey", { params: { page, keyword } });
and grab the data via req.query server-side.
const { page, keyword } = req.query;
With vanilla JS, you can use URLSearchParams to construct the query string...
const params = new URLSearchParams({ page, keyword });
// XHR
const xhr = new XMLHttpRequest();
xhr.open("GET", `/post/getPostsByKey?${params}`);
// Fetch
fetch(`/post/getPostsByKey?${params}`); // GET is the default method
Your Axios instance creation could also be a lot simpler...
Axios is usually quite good at setting the correct content-type header, you don't have to
Your Express app isn't doing any content-negotiation so you don't need to set the accept header
Unless you're actually using cookies (which it doesn't look like), you don't need credential support
const API = axios.create({ baseURL });

Axios call to set authorisation header not working properly

I have a React Native app using Axios.
Im trying to authenticate the user and then set the authorization header from the response but im getting a weird order of execution.
The setClientToken is not being called before the code after it to do a get request is to be called. ie this is what i get logged:
Loggin in....
Authenticating...
Getting Recipes...
Auth... undefined
Done.
... [Error: Request failed with status code 401] //getRecipes call
Setting token
So you see setting token is done last and that means every axios after that will work but not the recipe.js call.
//app.js
async componentDidMount() {
console.log("Loggin in....");
await LogIn(getUser().username, getUser().password);
console.log("Done.");
}
render () {
//Recipe component rendered.
}
//recipesApi.js
export function getRecipes(category, offset, count) {
console.log("Getting Recipes...");
const url = `search?category=${category}&currentPage=${offset}&pageSize=${count}`;
console.log("Auth...", APIKit.defaults.headers.common["Authorization"]);
return APIKit.get(getUrl(url));
}
//user.js
import APIKit, { setClientToken } from "./apiKit";
export default async function LogIn(email, password) {
console.log("Authenticating...");
APIKit.post("/users/authenticate", {
username: email,
password: password,
})
.then((token) => setClientToken(token))
.catch((error) => console.log(error));
}
//APIKit.js
import axios from "axios";
// Create axios client, pre-configured with baseURL
let APIKit = axios.create({
baseURL: "http://192.168.1.4:4000/api",
timeout: 10000,
});
APIKit.interceptors.response.use(function (response) {
return response.data;
});
export const setClientToken = (token) => {
console.log("Setting token...", token.token);
APIKit.interceptors.request.use(
function (config) {
config.headers.Authorization = `Bearer ${token.token}`;
return config;
}, null, { synchronous: true }
);
};

multiple url variable async/await axios

Probably this is a very stupid question, i'm new in Node.js and javascript, so please forgive me if the question is not properly explained or the answer is simple...
I'm trying to send 2 variables thru a url... When i send only 1 variable (artist=${term}) work all good, but I'm really stuck with about how to send 2 variables thru the url (&artist=${term1}&album=${term2})
I've work on this code so far which for 1 variable is working well... but i have no idea how to add a second or a third variable to the request:
File 1: "./services/albumInfo.js"
import { BRV_API } from '../../config';
import axios from 'axios';
import dotenv from 'dotenv';
const ALBUM_INFO = 'method=album.getinfo';
dotenv.config();
const doRequest = async (url) => {
return await axios.get(`${BRV_API}/${url}&api_key=${process.env.API_KEY}&format=json`);
};
export const infoAlbum = async (term) => {
return await doRequest(`?${ALBUM_INFO}&artist=${term}`);
};
File 2: "./repositories/albumInfo.js"
import { infoAlbum } from '../repositories/albumInfo';
import status from 'http-status';
export const albumInfo = async (req, res, next) => {
try {
const { query } = req;
const { data } = await infoAlbum(query.name);
const response = data;
res.status(status.OK).send(response);
} catch (error) {
next(error);
}
};
I know that my problem is in this part of the code (I guess)
export const infoAlbum = async (term) => {
return await doRequest(`?${ALBUM_INFO}&artist=${term1}&album=${term2}`);
};
I've been searching, and i've seen some solution, like this one, but i just don't understand those solutions or how to apply on my code (sorry for that, im a very new on this)
Any good soul who can help this newbie? (if can explain the why of the solution as well, for understand, will be amazing!!)
Thanks in advance!!
Axios provides parameters that can be added custom as the following
const your_url = process.env.URL
const infoAlbum = await axios.get(your_url,{
params: {
artist: term,
album: term2,
api_key: process.env.API_KEY,
format:'json'
}
})
console.log(infoAlbum.data.args)
note: your_url without any more parameters.
So,
I've found a solution, which is pretty ugly, but so far is working, if someone have a better option, will be amazing to know:
File 1: repositories/albumInfo.js, I've just add the console (as per #Alex028502 suggestion), to know what the code was returning:
import { BRV_API } from '../../config';
import axios from 'axios';
import dotenv from 'dotenv';
const ALBUM_INFO = 'method=album.getinfo';
dotenv.config();
const doRequest = async (url) => {
const fullurl = `${BRV_API}/?${ALBUM_INFO}${url}&api_key=${process.env.API_KEY}&format=json`;
console.log('full url is', fullurl);
return await axios.get(fullurl);
};
export const infoAlbum = async (term) => {
return await doRequest(`&${term}`);
};
File 1: services/albumInfo.js: I change the behaviour of 'infoAlbum' to make the request from his side:
import { infoAlbum } from '../repositories/albumInfo';
import status from 'http-status';
export const albumInfo = async (req, res, next) => {
try {
const { query } = req;
console.log(query);
const { data } = await infoAlbum('artist=' + query.artist + '&album=' + query.album);
const response = data;
res.status(status.OK).send(response);
} catch (error) {
next(error);
}
};
I know that probably this is not the very best way to walk away from the problem, but so far is what i have.... any other better option about how to capture the second or third parameter of the url request and then add them to the final url?
Best!

Not receiving the data from Node JS and use it in React JS

I'm trying to figure out how to connect NodeJS and send some data to React JS for it to use. The information is being sent when I access the backend, but React JS receives an empty object {}. I'm not sure what I'm doing wrong. Might the problem be related to CORS? Or do I have to use JSON.parse(r)? Not sure.
index.js
const a = "sup"
app.get("/", (req,res)=>{
console.log("someone is here")
res.status(200).json({
data:a
})
})
app.listen(3000, ()=>{
console.log("Server is running")
})
Homepage.jsx
class Homepage extends Component {
state = {
authenticated: false,
data:""
};
async componentDidMount(){
const url = "http://localhost:3000"
const r = await fetch(url, {
mode: "no-cors",
method: "GET",
headers:
{"Access-Control-Allow-Origin": "*"}
})
const data = await JSON.stringify(r)
console.log(data)
}
render() {
return ( <h1>{this.state.data}</h1> );
}
}
UDPATE:
I had a port issue usage issue and incorrect usage of componentDidMount(). I managed to improve the code as recommended by users. NodeJS and ReactJS were pointing to port 3000. I reassigned the ports (NodeJS:3000, ReactJS:4000). ReactJS is now making a fetch call to "http://localhost:3000". However, I now get 2 errors:
1) Failed to load resource: net::ERR_EMPTY_RESPONSE
2) Uncaught (in promise) TypeError: Failed to fetch
index.js
const express = require("express")
const app = express()
const cors = require("cors")
const a = "sup"
app.use(cors({
origin:"http://localhost:4000",
methods:"GET,HEAD,PUT,PATCH,POST,DELETE",
credentials:true
}))
app.use((req,res,next)=>{
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization")
})
app.get("/", (req,res)=>{
console.log("someone is here")
res.status(200).json({
data:a
})
})
app.listen(3000, ()=>{
console.log("Server is running")
})
Homepage.jsx
import React, { Component } from 'react';
class Homepage extends Component {
state = {
data:[]
};
componentDidMount(){
const url = "http://localhost:3000"
fetch(url)
.then(r=>r.json())
.then(data=>this.setState({data}))
}
render() {
return ( <h1>{this.state.data ? this.state.data : "loading"}</h1> );
}
}
export default Homepage;
class Homepage extends Component {
constructor(props){
super(props);
this.state = {
authenticated: false,
data: ""
};
}
componentDidMount() {
const url = "http://localhost:3000"
fetch(url)
.then(response => response.json())
.then(data => this.setState({
data: data
}));
}
render() {
return ( < h1 > {
this.state.data ? this.state.data : 'loading';
} < /h1> );
}
}
Please, update your class component. The fetch in componentDidMount() was not as expected. You need not use await in fetch because, this async action when ever its done, the state gets updated and react renders the updates. Please, watchout for the response you send via API and set the state accordingly. Here, I have set to data considering the api sample you have provided in the question. Hope this helps!
Make sure you have express installed and initialised into const app.
Then:
On the backend:
const data = "Sup"
app.use(
"/",
(req, res)=>{
res.status(200).send(data);
}
);
On the front end- Inside componentDidMount:
const url="http://localhost:3000"
axios.get(url).then(response => response.data)
.then((data) => {
this.setState({ data: data })
console.log(this.state.users)
})

How to make call to external api from nodejs

Hi all I have to develop a utility which makes a call to external API with different parameters, for example, I have an array val which has 100 value val= ['we23','22ww', 'gh22'....n] and URL: www.google.com so one by one I have to append value from val to the URL, first api= www.google.com/we23, second api= www.google.com/22ww and make an External API hit and then store the response in database. so what is the most efficient way to do it? and links to working examples would be helpful.
A very simple example express app using the Fetch API:
const express = require('express')
const fetch = require('node-fetch')
const app = express()
// This sets up a route to localhost:3000/random and goes off and hits
// cat-fact.herokuapp.com/facts/random
app.get('/:apiRoute', async (req, res) => {
try {
const { apiRoute } = req.params
const apiResponse = await fetch(
'https://cat-fact.herokuapp.com/facts/' + apiRoute
)
const apiResponseJson = await apiResponse.json()
// await db.collection('collection').insertOne(apiResponseJson)
console.log(apiResponseJson)
res.send('Done – check console log')
} catch (err) {
console.log(err)
res.status(500).send('Something went wrong')
}
})
app.listen(3000, () => console.log(`Example app listening on port 3000!`))
Visit http://localhost:3000/random
With the following code you can make concurrent API calls within an endpoint using Node.js + Express:
const [
LoMasNuevo, LoMasVisto, TeRecomendamos, Categorias,
] = await Promise.all([
numerosController.getLoMasNuevo(),
numerosController.getLoMasVisto(),
numerosController.getRecomendaciones(),
categoriasController.getCategorias(),
]);
Inside every get function you can make an axios request like this:
const params = {
method: 'GET',
url: 'https://development.api.yodlee.com/ysl/transactions',
headers: {
'Api-Version': '1.1',
Authorization: `Bearer ${tokenuser}`,
},
};
const data = await axios(params);
return data;
In 2022
In Node.js:
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) =>
fetch(...args));
app.get('/checkDobleAPI', async (req, res) => {
try {
const apiResponse = await fetch(
'https://jsonplaceholder.typicode.com/posts'
)
const apiResponseJson = await apiResponse.json()
console.log(apiResponseJson)
res.send('Running 🏃')
} catch (err) {
console.log(err)
res.status(500).send('Something went wrong')
}
})
You can use Express to build a API as your idea
Then you can call api by using axios package.
In addition, you can build link to receive request and send response by using Router of ExpressJS

Categories