I'm a little new to creating a backend in Node/Express, but I am trying use axios to make HTTP requests. I've set up express routes that will make the necessary request and I know from using Postman that GET request I'm testing does return a response. Where I'm stuck is how to return that data and send it to my React/Redux app to use.
-Server Side-
//Express Route
app.get('/api/recipes', recipeController.getRecipes)
//Controller Function that makes axios request
const axios = require('axios')
const Promise = require('bluebird')
module.exports = {
getRecipes(req, res) {
const url = "https://gw.hellofresh.com/api/recipes/search?country=us&limit=9"
const token = "IUzI1NiIsInR5c"
axios
.get(url, {
"headers": {"Authorization": "Bearer " + token}
})
.then((response) => {
console.log(response)
})
.catch((err) => {
console.log(err)
})
}
}
-Client Side-
I dispatch the following action and make a call using the endpoint I created. However, at this point, I'd get an error status even though on the server side I was able to get a response. I tried playing around using Promises as I read that axios GET requests returns promises, but couldn't wrap my head around on how to implement it.
export const getRecipes = () => {
return (dispatch) => {
axios
.get('/api/recipes')
.then((resp) => {
console.log(resp)
})
.catch((err) => {
console.log(err)
})
}
}
You need to call res.send in the route, to send the data to the client:
module.exports = {
getRecipes(req, res) {
const url = "https://gw.hellofresh.com/api/recipes/search?country=us&limit=9"
const token = "IUzI1NiIsInR5c"
axios
.get(url, {
"headers": {"Authorization": "Bearer " + token}
})
.then(response => {
console.log(response)
res.send(response) // <= send data to the client
})
.catch(err => {
console.log(err)
res.send({ err }) // <= send error
})
}
}
Related
I have hosted a JWT auth API on heroku. While accessing the API using axios from my front-end React app, I notice that the protected routes can never be fetched since they are dependent on res.cookies which are never set. While testing the API out on postman, it sets the cookies and everything works fine. Can you help me finding where I am going wrong and how I can overcome this.
Sample cookie that is supposed to be set(working fine with postman)
This cookie is needed, because when I GET /user, with the following code:
exports.checkUser = catchAsync(async (req, res, next) => {
let currentUser;
if (req.cookies.jwt) {
const token = req.cookies.jwt;
const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
currentUser = await User.findById(decoded.id);
} else {
currentUser = null;
}
res.status(200).send({ currentUser });
});
currentUser should be populated as follows
When I access this API using my react frontend: this being my useAuth hook
import { useState, useContext } from "react";
import { useHistory } from "react-router-dom";
import axios from "axios";
import { UserContext } from "./userContext";
export default function useAuth() {
let history = useHistory();
const { setUser } = useContext(UserContext);
const [error, setError] = useState(null);
const setUserContext = async () => {
return await axios
.get("<my api link>/user")
.then((res) => {
console.log(res);
setUser(res.data.currentUser);
history.push("/home");
})
.catch((err) => {
console.log(err);
setError(err);
});
};
const registerUser = async (data) => {
const { username, email, password, passwordConfirm, name } = data;
return axios
.post("<My api link>/auth/post", {
username,
email,
password,
passwordConfirm,
name,
})
.then(async (res) => {
console.log(res);
await setUserContext();
})
.catch((err) => {
console.log(err);
return setError(err);
});
};
res.data.currentUser is ALWAYS null. Any help is appreciated
The concept of JWT is that the handling of the token is handed over to the client.
Hence, the frontend is supposed to store/save the JWT Token it receives in cookies or localstorage and is supposed to send them via headers in requests where you want to access such protected routes.
Hence it's the frontend duty and that's why Postman handles it automatically for you.
You can use react-cookie to save and retrieve this JWT token in the frontend whenever required and you will have to reform your axios request.
In your specific case, you can do the following for GET request:
axios.get('URL', {
withCredentials: true
});
But I would strongly recommend amending your backend to extract JWT tokens from headers instead of cookies, which would make your request, something similar to:
let JWTToken = 'xxyyzz'; // Get this from cookie or localstorage, hardcoded for demonstration.
axios
.get("URL", { headers: {"Authorization" : `Bearer ${JWTToken}`} })
.then(res => {
this.profile = res.data;
console.log('Fetched Data is', res.data);
})
.catch(error => console.log(error))
Do not forget to enable CORS on your backend!
I am trying to make my server a middleman so that the frontend queries the server with a searchedValue, the server queries the API with the searchedValue, and the server returns the API response to the frontend.
Currently, the server is querying the API correctly. Here is the code and responses:
Query: http://localhost:3000/optionsAPI/AAPL
Code [server.js]:
app.get("/optionsAPI/:ticker", (req, res) => {
var tempJSON = [];
const searchString = `${req.params.ticker}`;
const url = `API URL HERE, HIDING FOR SECURITY`;
fetch(url, { headers: { Accept: 'application/json' } })
.then(res => res.json()
.then((json) => {
tempJSON = json;
console.log(tempJSON);
}))
.catch(err => console.error(err)); // eslint-disable-line
res.send({ message: tempJSON });
});
Here is the code in the component:
Code [Component.js]:
useEffect(() => {
const fetchData = () => {
const url = `/optionsAPI/${searchedValue}`;
fetch(url, { headers: { Accept: 'application/json' } })
.then(res => res.json()
.then((json) => {
setOptions(json.option_activity || []);
}))
.catch(err => console.error(err)); // eslint-disable-line
};
debounce(fetchData());
}, [searchedValue]);
The console log is perfect! It logs tempJSON as I would expect to see it, but the res.send message is simply {"message":[]}. Therefore, the response my frontend gets is an empty []. This doesn't; make sense - the console is logging the response, so why is the frontend receiving a blank []?
Thanks in advance.
In your code you are calling an api which returns a promise, so to handle the data returned by the promise you should add your code inside .then() function, meaning you have to wait for the promise to be resolved before accessing the data and sending it to the client
app.get("/optionsAPI/:ticker", (req, res) => {
var tempJSON = [];
const searchString = `${req.params.ticker}`;
const url = `API URL HERE, HIDING FOR SECURITY`;
fetch(url, { headers: { Accept: 'application/json' } })
.then(res => res.json()
.then((json) => {
tempJSON = json;
console.log(tempJSON);
// the response should be sent from here
res.send({ message: tempJSON });
}))
.catch(err => {
console.error(err);
// you also need to send a response when catching erros
res.status(400).send({ err });
});
});
you can use async / await to make your code much cleaner
app.get("/optionsAPI/:ticker", async (req, res) => {
var tempJSON = [];
const searchString = `${req.params.ticker}`;
const url = `API URL HERE, HIDING FOR SECURITY`;
try {
const result = await fetch(url, { headers: { Accept: 'application/json' } })
const json = await result.json()
tempJSON = json;
console.log(tempJSON);
res.send({ message: tempJSON });
} catch (error) {
console.error(error);
res.status(400).send({ error });
}
});
I trying to send a request to an api and return the response with express when someone curl my site.
app.post('/', async (req, res) => {
request.get('the_api')
.on('response', function(resp){
return res.status(200).send({
text: resp.body
})
})
})
For that requirement, in my projects I use axios, from the documentation:
Axio is a promise based HTTP client for the browser and node.js.
Here is a complete example:
const axios = require('axios');
// api openexchange
const oexchange = axios.create({
baseURL: 'https://openexchangerates.org/api/',
timeout: 60000
})
oexchange.interceptors.request.use((config) => {
config.params = config.params || {};
config.params.app_id = 'myapitoken'
return config;
});
app.get('/', async (req, res) => {
try {
//here it will request:
//https://openexchangerates.org/api/latest.json?app_id=myapitoken
req.oexchange = await oexchange.get('latest.json')
return res.status(200).json(req.oexchange.data)
} catch (e) {
res.status(500).json({ errors: [{ location: 'cotacao', msg: 'Houve um erro ao acessar a api do open exchange.', param: 'openexchangerates' }] })
}
})
In my example I am requesting an external exchange api.
Here is the documentation from axios.
Hope it helps.
Here is my React code for the form submission:
const handleSubmit = (e) => {
e.preventDefault();
console.log('item:', item);
Axios.post('http://<MY_SERVER>/item/add', {name:item})
.then(response => console.log(response))
.catch(err => console.log(err));
};
and this is the code in my Node API:
// Add a new Item
app.post('/item/add', (req, res) => {
const newItem = new Item({
name: req.body.name
});
newItem.save()
.then(item => {
res.json({msg: 'success'});
})
.catch(err => console.log(err));
});
When I run the handleSubmit nothing happens. I only get the console.logs... Also, here is the error from my server
'ValidationError: item validation failed: name: Path' `name` is required
So it is clear that the data sent over to the api is never received. I've tried changing it up in many ways I have came across online but no luck.
I have attached both ways to post data i.e. Form URL Encoded and JSON. For sending Form Url Encoded data we need an additional Library querystring.
You can install it using npm install query-string
Here is the code for both the requests. You don't need query-string if you are using content type application/json.
Here you go
var axios = require('axios');
const qs = require('querystring');
function sendFormUrlEncodedData() {
const headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
const payload = {
name: 'morpheus',
job: 'leader'
};
//Send data with form url using querystring node package for it.
axios
.post('https://reqres.in/api/users', qs.stringify(payload), {
headers: headers
})
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});
}
function sendJSONData() {
const headers = {
'Content-Type': 'application/json'
};
const payload = {
name: 'morpheus',
job: 'leader'
};
//Send data with JSON, so stringifying it.
axios
.post('https://reqres.in/api/users', JSON.stringify(payload), {
headers: headers
})
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});
}
sendFormUrlEncodedData();
sendJSONData();
First of all check whether your backend code is working or not by using postman. I think you are getting validation error because of the error of your backend code. And also check whether that you are implemented the name attribute correctly with its data type.
After that update, the react code as below.
import axios from 'axios';
constructor() {
this.item = {
name: ''
}
}
handleSubmit(event) {
console.log('item:', this.item.name);
event.preventDefault();
axios.post('http://<MY_SERVER>/item/add', this.item)
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
}
I have a problem, when I post my form, i can't get what is in my post in my API.
this is my post with axios in react :
onSubmit = () => {
let data = {
nickname: this.state.nickname,
password: this.state.password
}
axios.post("/api/user/login", { data })
.then(res => {
console.log("res from api is => ", res.data);
})
}
and this is in my API :
import koaBody from "koa-body";
const app = new koa();
const router = new Router();
app.use(router.routes());
app.use(koaBody());
router.post("/api/user/login", async(ctx) => {
console.log("body is => ", ctx.request.body);
ctx.body = "ok";
});
the problem is ctx.request.body is always undefined...
Can you tell me why ?
I tried with router.get and I have no problem, it works fine.
You need to load the body parser before the router, otherwise the router will get to handle the requests before the body contents are parsed:
app.use(koaBody());
app.use(router.routes());