How to access json response data using Axios, node/express backend - node.js

I have this project I’m working on and I am using node/express + Axios to retrieve data from a third-party API.
I am attaching an image of the response I am getting from my postman but,
I am having an issue figuring out a way to access and manipulate a specific set of data.
If there are any resources anyone could share that would help I would appreciate it.
as of now I just have:
axios.get('apiUrl')
.then((response) => {
const cardData = response.data;
res.send(cardData);
}
This is the response I get:
for example, I’d like to access the “abilities” property.
Since that property is within the “0" object within the response object, I’m a bit confused as to how to navigate this in the code.
I’ve tried response.data.0 but that doesn’t seem to work.

function retrieve(callback){
//I don't get why you are using request.send here. Are you routing the response elsewhere?
//If you are just consuming a service, use Axios with a callback instead.
//If you're not routing it you won't need Express.
axios.get('apiUrl').then(response => callback(response));
}
function clbk(response){
let obj = JSON.parse(response); //In case you are receiving a stringified JSON
//Do whatever you want with the data
//If you have a number as a key, access it by using []. So, considering your example:
response.data[0]
}
//CALL:
retrieve(clbk);

Related

How to display binary images retrieved from API in React.js?

✨ Hello everyone!✨
General Problem:
I have a web app that has about 50 images that shouldn't be able to be accessed before the user logs into the site. This should be a simple answer I suspect, there are plenty of sites that also require this basic protection. Maybe I do not know the right words to google here, but I am having a bit of trouble. Any help is appreciated.
App details:
My web app is built in typescript react, with a node.js/express/mongoDB backend. Fairly typical stuff.
What I have tried:
My best thought so far was to upload them into the public folder on the backend server hosted on heroku. Then I protected the images with authenication middlewear to any url that had "/images/" as a part of it. This works, partially. I am able to see the images when I call the api from postman with the authenication header. But I cannot figure out a way to display that image in my react web app. Here is the basic call I used.
fetch(url,
{
headers: {
Authorization:token,
},
}
);
and then the actual response is just an empty object when I try to copy it
{}
but I also get this when I console log the pure response, some kind of readable stream:
from following related question
I came up with the following: (which is normally wrapped in a asyc function)
const image = await fetch(url,{headers:{ Authorization:token}});
const theBlob = await image.blob();
console.log(URL.createObjectURL(theBlob));
which gives me the link: http://localhost:3000/b299feb8-6ee2-433d-bf05-05bce01516b3 which only displays a blank page.
Any help is very much appreciated! Thanks! 😄
After lots of work trying to understand whats going on, here is my own answer:
const image = await axios(url, { responseType: "blob", headers: {Authorization: token }});
const srcForImage = URL.createObjectURL(image.data)
Why it makes sense now
So I did not understand the innerworkings of what was going on. Please correct me, but the following is my understanding:
So the image was being sent in binary. What I had to do to fix that was to set the reponseType in axios as "blob", which then sent a blob, which I believe means its base 64 encoded instead. Then the function URL.createObjectURL does some magic, and must save it to the browser as part of the page. Then we can just use that as the image url. When you visit it yourself, you must type the 'blob:' part of the url it give you too, otherwise its blank, or stick it in <img src={srcForImage}/> and it works great. I bet it would've worked in the original fetch example in the question, I just never put the url in a tag or included 'blob:' as part of the URL.
That's correct, you send the auth token and the backend uses that to auth the user (check that he exists in the DB, that he has the correct Role and check the jwt too)
The server only responds with the images if the above is true
If your server is responding with an empty object then the problem is the backend not the frontend, console.log what you're sending to the frontend

use req.body data from the api route handler in a external javascript file in nextjs api

I need to send the req.body (data received from the client to API route via post request) data from the API route to a separate javascript file in the backend api.
const payload = {}
const handler = async (req, res) => {
payload = req.body
res.status(200).json({ status: 'success' })
}
export default handler
I declared a variable outside the route handler function and assigned req.body to it, but then I realized that I can't use this global variable inside the handler function. I don't know the reason. Is there any specific way of achieving what I'm trying to achieve here?
I request to please elaborate the use case a bit more. Based on current requirements.
I can understand that-
You need to send the req.body to another JavaScript file. Now here can be 2 cases.
You need to store req.body in a file, and use it for later processing.
Pass the req.body to another function which is present in another JavaScript file.
In this case, you can export the function from another file and import in this main file and call the function in your controller and pass req.body as Payload.
I think your use case will be second one, if not please update question and I will be happy to help you out.

NodeJs with Express not parsing form data from node-fetch

I'm creating two APIs with NodeJS, Express, and TypeScript. One API will take a request with a content type of multipart/form-data, and will use the body in the request to make another request to a second API.
I'm using Postman, so the chain of request looks something like this
Postman -> First API -> Second API
I use node-fetch to make a request from the first API to the second one. The body of the request is a FormData, which contains some files and key-value pairs.
const form = new FormData();
// File for profile picture
const profilePictureBuffer = await (await fetch(user.profilePicture)).buffer();
form.append('profilePicture', profilePictureBuffer);
// File for ID Card
const idCardBuffer = await (await fetch(user.idCardUrl)).buffer();
form.append('idCard', idCardBuffer);
// This part iterats over the obsect of 'user',
// which contains other key-value pairs
Object.entries(user).forEach((data) => {
form.append(data[0], data[1]);
});
// Make POST request to second API
const pinterUser = await fetch(secondAPIURL, {
method: 'post',
body: form,
headers: form.getHeaders()
});
I ran both of the APIs on localhost so that I can monitor the logs for any bugs. As I make a request from Postman to the first API, then the first API make another request to the second API, I got the following error log in the terminal for the second API
TypeError: Cannot read property '0' of undefined
After some investigation, I found out that, in the second API, the req.body and req.files are empty objects. This means that Express did not parse the incoming request. Note that I've also already a multer middleware to handle the files in the request.
Furthermore, I have added the following lines of code in my server.ts file for the second API
/** Parse the body of the request */
router.use(express.urlencoded({ extended: true }));
router.use(express.json());
However, when I tried making the request from Postman, it returns a successful response.
I'm not really sure what's going on here. I've tried looking for some answer regarding this similar issue, but most of them suggest adding urlencoded and json, or using some other library to handle parsing form data.
In my case, the first suggestion doesn't solve my problem since I already added them from the start, and the latter is what I'm trying to avoid.
I wonder if anybody could point out what I was missing here? Thanks in advance

How do I properly proxy a request to the Google Maps Embed API?

I'm attempting to use the Google Maps Embed API to embed a map matching an address that a user enters. Following the developer guide, I acquired an API Key to add to my API requests. I'm able to successfully pull up a map when I request it via the "src" attribute of an iframe in my React client, like so:
<iframe
...
src={`https://www.google.com/maps/embed/v1/${mode}?key=${apiKey}&q=${encodedAddressQuery}`}
>
But this leaves my API key exposed to the client.
In Google's API Key Best Practices, it's recommended that the API key be stored in an environment variable and that a proxy server be used to safeguard keys. However when I try proxying the request (code below), it seems to return the appropriate map for a split second, but then the iframe replaces the map with an "Oops! Something went wrong" message, and the browser console displays this error:
Google Maps JavaScript API error: UnauthorizedURLForClientIdMapError
https://developers.google.com/maps/documentation/javascript/error-messages#unauthorized-url-for-client-id-map-error
Your site URL to be authorized: http://127.0.0.1:3001/api/maps?parameters=[my_encoded_parameters]
I'm just developing locally at the moment, so I've tried registering http://127.0.0.1:3001/* as an authorized URL as the error documentation suggests, and I've also tried removing all website restrictions on the API key just to see if I was authorizing the wrong URL, but both of those attempts still produced the same error.
I haven't found many resources on setting up a proxy other than this Google Codelabs project, but I haven't been able to pick anything out of it to help with this issue.
Here's the code I'm using in my React front end:
<iframe
...
src={`http://127.0.0.1:3001/api/maps?parameters=${encodedAddressQuery}`}
>
And in my Express Node.js back end:
router.get('/api/maps', async (req: Request, res: Response) => {
try {
const parameters = req.query.parameters;
const map = await MapDataAccessObject.getOne(parameters);
return res.status(OK).send(map.data);
} catch (err) {
...
}
});
export class MapDataAccessObject {
public static async getOne(parameters: string) {
const apiUrl = this.getMapsEmbedUrl(parameters);
const map = await axios.get(apiUrl);
return map;
}
private static getMapsEmbedUrl(parameters: string) {
const encodedParams = encodeURI(parameters);
return `https://www.google.com/maps/embed/v1/place?key=${process.env.MAPS_API_KEY}&q=${encodedParams}`;
};
}
I'm running my React front end and my Node.js back end in Docker containers.
My best guess is that I'm missing a request header that the Maps API is expecting, but I haven't found any documentation on that, or maybe I'm misunderstanding something more fundamental. Any guidance would be very much appreciated.
There is no way to protect your google maps key on a website from being "spied on" because it is always public. The only way to protect your key from foreign usage is to restrict it's access to only the allowed domain/ip-addresses - so if it is used from someone else, it will not work or take anything from your credits (google will show an error message).
https://developers.google.com/maps/documentation/javascript/get-api-key

Can't create a listener to the database while also being able to update it. (using References.on and References.put)

I'm trying to display a list of comments on my react page.
For this I have setup a NodeJS server which loads the data from Firebase and passes it on to React. I am able to get it to load the comments list and display them, but when I try to add a comment, the server crashes with the following error:
#firebase/database: FIREBASE WARNING: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
This is because I am using:
firebase.database().ref('my-path').on("value", ...)
However, if I use firebase.database().ref('my-path').once("value", ...) then I lose the ability to update the comments as soon as a new comment is posted. Is there a way to be able to have a listener attached to the database and still be able to update the contents of that database?
Here is my NodeJS code:
app.get("/comments/:id", (req, res) => {
const itemsRef = firebase.database().ref(`comments/${req.params.id}`);
itemsRef.on('value', (snapshot) => {
let comments = snapshot.val();
return res.status(200).json(comments);
})
})
app.post("/comments/:id", (req, res) => {
const itemsRef = firebase.database().ref(`comments/${req.params.id}`);
itemsRef.push(req.body);
})
The error occurs after the post request is called.
You're sending a response back to the client with:
res.status(200).json(comments)
This sets at least two headers (the status, and the response type) and then sends the response. Next time you get an update from the database, this code runs again, and again tries to send the two headers. But in HTTP all headers must be before the main body of the response. So the second time this code runs, it throws an error.
If you want to keep sending more data to the client, you'll need to use more primitive methods of the response object to prevent sending headers, or other illegal data. While possible, it's more complex than you may think, as the client needs to handle this response stream, which most clients won't.
I'd highly recommend looking at Doug's alternative, which is to just use the Firebase Realtime Database from the client directly. That way you can use the client SDK that it has, which handles this (and many more complexities) behind the scenes.

Resources