Trouble understanding loading json with promises express - javascript

In my nodeJs app I create a post request;
const express = require('express');
const app = express();
app.listen(80, () => console.log('Listening at 80'));
var movies = require("G:/path/to/json/movienames.json");
app.use(express.json({ limit: '1mb'}));
app.post('/movies', (request, response) => {
console.log('I got a request!');
console.log(request.body);
response.json(
movies
);
});
Now in my Javascript file i need to receive the promise and get the data with .then with a function call to convert it to json. Then it sends the data. I guess?
const postMovieLoad = {"Movies has been loaded": "True"};
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postMovieLoad)
};
moviesJson = fetch('/movies', {
method: "POST", // "GET/POST"
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postMovieLoad)
})
.then(moviesJson => moviesJson.json())
.then(moviesJson => {
Json = {//Json copied directly from file};
console.log(Json === moviesJson); //logs false
console.log(Json); // logs {movies: Array(193)}
console.log(moviesJson); // logs {movies: Array(193)}
console.log(typeof Json, typeof moviesJson); //logs object object
}).catch(error => console.error('Error', error))
I fetch the promise. The syntax is confusing me alot here. I'm a little unsure about the .then function stringed together. I think its like a handshake between the server and the client.
What is really stumping me though, is why the file has been modified and why? Both are json objects, both have the same contents. I just want to get the json object to be assigned to a variable client side from server side.

When you do that (a === b), you are comparing that a and b are the same object, that is, the same reference in memory, since the objects in javascript are references, in your case a and b do not have the same memory address, then you will not be able to do it that way JSON.stringify (a) == JSON.stringify (b) could help, you could add a trim to be safer since there you convert it to string and the comparison is not by reference, its by characters length and order

Related

Problem returning response from POST request with Axios in Node.js

I am writing a simple post request in a Firebase Cloud function, with Axios. This function calls an API endpoint and gets an object of profile details as response. My problem is to correctly return the response to the client.
In the code below, the Cloud Function logs the result correctly. But I can't figure out how to correctly return it to the client from the client-side callGetProfile() function. (Which runs inside a Vue3 method.)
I am probably missing something obvious but am very new to Node.js and HTPP requests.
Thanks for any help!
// MY FUNCTION IN NODE.JS (Firebase Cloud Functions)
exports.getProfile = functions.https.onCall((data, context) => {
var postData = {
profile_id: "xxxxxxxxxxxxxxxxx", //hardcoded here for testing but should be passed in "data" arg.
profile_type: "personal",
};
let axiosConfig = {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'X-API-KEY': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX'
}
};
axios.post('xxxxxxxxxxxxxxxxxxxxxxxxxxxxx', postData, axiosConfig)
.then((res) => {
console.log(res.data) // this works, I get all the data correctly!!
return res // also tried res.data
})
.catch((err) => {
console.log("AXIOS ERROR: ", err);
})
});
// MY FUNCTION CLIENT SIDE (Vue3 method)
const functions = getFunctions();
const callGetProfile() = httpsCallable(functions, "getProfile");
callGetProfile()
.then((result) => {
console.log(result.data) // this doesn't work, data is "null"
})
.catch((e) => console.log(e));

Send an Object with Blobs in it to server

I have a data object containing strings, booleans, arrays, objects and even Blobs like this:
Now I want to send this object to my Node.js server...
Using traditional fetch API or Ajax I'm unable to send the blobs and they appear like an empty object server side!
I tried to use fetch API even using FormData approach like:
Client:
const fd = new FormData();
fd.append("upload", data); // data is the object to send
fetch('/mother/new-card', {
method: 'post',
body: fd,
})
Server:
controller side:
router.post('/new-card', newCard);
function newCard(req, res) {
motherService.newCard(req)
.then(result => res.json(result))
.catch(err => {
console.log(err);
res.json(err)
});
}
Service side:
async function newCard(req) {
console.log('req.body', req.body);
return { success: req.body }
}
I usually get undefined trying above methods ...
Is there any valid way to send an object with blobs in it?

Building API end point in Firebase Functions that receives values and calculates them

i am trying to build a function/endpoint that receives some values, calculates them, and returns a pass/fail and a message, hopefully it works like this:
export const processLoanRequest = functions.https.onRequest(async (request, response) => {
// Receive values
// determine if values they pass a criteria
// returns true (pass) or false (fail), as well as a message that says why
})
I tried a few things to see if I could work with it off the bat, but I don't think I even came close, like this one:
export const processLoanRequest = functions.https.onRequest((request, response) => {
console.log({ request, response });
const obj = {
name: 'the object',
param: 'object param',
};
response.json(obj);
});
The function I wrote in my React app to post a request is copied from MDN Web Docs -- Using Fetch and looks like:
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST',
mode: 'no-cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: JSON.stringify(data),
});
return response.json();
}
const handleSubmit = values => {
console.log(values);
// postLoanInfo(values).then(data => console.log(data));
postData(
'https://us-central1-autoloan-24e0d.cloudfunctions.net/processLoanRequest',
{ ...values }
)
.then(data => {
console.log(data); // JSON data parsed by `data.json()` call
})
.catch(err => console.log(err));
};
With those I always ended up with an error message that says:
SyntaxError: Unexpected end of input
at LoanForm.js:35
at s (runtime.js:63)
at Generator._invoke (runtime.js:293)
at Generator.next (runtime.js:118)
at r (asyncToGenerator.js:3)
at u (asyncToGenerator.js:25)
From the catch(err...), and line 35 is return response.json(); in postData(..). What happens to the body when the request is posted? How is it received at the end point and how do I access it from there?
The values object looks like:
{purchasePrice: 12000, autoMake: "kia", autoModel: "soul", yearlyIncome: 50000, creditScore: 680}
Thanks!
Update:
Turns out that I had to implement Rafael's solution below, as well as response.set('Access-Control-Allow-Headers', 'Content-Type'); to get it to work. I am not very experienced so I did not think to look under the network tab in the dev tools, which gave indicated what had to change.
Update2:
response.set('Access-Control-Allow-Headers', 'Content-Type'); is only needed if you're posting a request that has headers (i.e. has a body that contains values). It isn't necessary for the above example since I'm not posting anything and I'm only getting the value of obj in return.
Answering as Community Wiki as this is based on #RobertNubel's comment.
The error you are getting means that the response body is not valid JSON, which is likely to be caused by some other error in the background. You can open the browser's network inspector on your React site to monitor the API call as it's made to get more info on what's happening.
As you can see on this community question, this issue is likely to be caused by lack of CORS configuration on your response at the cloud function, in order to fix that, all you have to do is add a couple of extra lines to your functions as follows:
export const processLoanRequest = functions.https.onRequest((request, response) => {
console.log({ request, response });
const obj = {
name: 'the object',
param: 'object param',
};
response.set('Access-Control-Allow-Origin', "*")
response.set('Access-Control-Allow-Methods', 'GET, POST')
response.set('Access-Control-Allow-Headers', 'Content-Type')
response.json(obj);
});

Dynamically decide which Fetch response method to use

I work with a Backend API which returns different data types for different requests to the same endpoint. While a more appropriate solution would be to unify the data type returned, legacy, time and lack of tests play against this solution.
I am centralizing my call method to be used by other parts of the application which need to call the endpoint. This call method implements fetch. For information:
export default function call<P> (method: TCallMethod, payload: P, parameter?: string): Promise<IServerResponseObject> {
const url: string = buildUrl(parameter);
const body: string | null = payload ? JSON.stringify(payload) : null;
return fetch(url, {
method,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${getAuthToken()}`
},
body
}).then(async (response) => {
let body: IServerResponseObjectBody = {
message: '',
code: ''
};
if (response) {
body = await response.json();
}
return {
status: response.status,
body
};
});
}
As I receive data, I am using the Response.json method to decode it.
if (response) {
body = await response.json();
}
The problem is that sometimes I receive no data (when the user is not authenticated - although that's an edge case) or the server responds with just a boolean.
In that case, the json() execution fails, because we are not handling JSON data.
ie:
FetchError: invalid json response body at http://localhost:4545/api/definition/isNameUnique/used%20name reason: Unexpected end of JSON input
I am wondering if there is a cleaner way than nesting try/catches to determine which decode method to use from the ones available: https://developer.mozilla.org/en-US/docs/Web/API/Body#Methods
This feels like a potential solution: https://developer.mozilla.org/en-US/docs/Web/API/Body#Properties but the documentation is not too explicit and lacks examples on how to use it.
It sounds to me like you want to use text to read the response, then look at the resulting text and decide what to do. Roughly:
const text = await response.text();
if (!text) {
// no response, act accordingly
} else if (reBool.test(text)) {
// boolean response, determine whether it's true or false and act on it
} else {
// JSON response, parse it
data = JSON.parse(text);
// ...then use it
}
...where reBool is a regular expression to test for the boolean the server sometimes returns, for instance /^(?:true|false)$/i.
If the response may have whitespace, you might trim the result of response.text().
There are some unrelated things you might also want to do:
You're not checking for a successful response (this is a mistake a lot of people make, so many I wrote it up on my otherwise-anemic little blog). Check response.ok before using json or text, etc.
It doesn't make much sense to pass an async function into then as a callback. If you're going to go async, do it earlier, by making call an async function, then work with await throughout the body rather than mixing your metaphors...
Addressing those and folding in the main answer above (you'll need to adjust as necessary, either IServerResponseObject needs changing or you need to do something different with boolean responses):
const reBool = /^(?:true|false)$/i;
export default async function call<P> (method: TCallMethod, payload: P, parameter?: string): Promise<IServerResponseObject> {
const url: string = buildUrl(parameter);
const body: string | null = payload ? JSON.stringify(payload) : null;
const response = await fetch(url, {
method,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${getAuthToken()}`
},
body
});
const {status} = response;
if (!response.ok) {
throw new Error("HTTP error " + status); // Or `return {status};` or similar, but making it an error is useful
}
const text = (await response.text()).trim();
let result = {status};
if (!text) {
// blank, act accordingly, perhaps:
result.body = null;
} else if (reBool.test(text)) {
result.body = text === "true";
} else {
result.body = JSON.parse(text);
}
return result;
}

Spring MVC with Reactjs fetch error

I am getting a similar error from (here)[Getting "TypeError: failed to fetch" when the request hasn't actually failed
My method is annotated with #CrossOrigin
With postman my request works fine ( from locally)
see POST to http://star-is.info:8080/app-1.0.0-BUILD-SNAPSHOT/register with headers Content-Type application/x-www-form-urlencoded and passing a string with firstname
Locally works fine but my form (here)[http://star-is.info:8082/] does not
const data = {};
data['firstname'] = this.state.firstname;
console.log('submitSignup');
fetch('http://localhost:8080/app-1.0.0-BUILD-SNAPSHOT/register', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then((response) => response.json()
.catch(err => {
console.err(`'${err}' happened!`);
return {};
})).then(function (body) {
console.log(body);
})
.catch(error => {
alert(error);
});
Now I am getting a reply from server
{firstname: null}
but why is firstname not being passed to the server..
The way i achieved this much as using register as endpoint to call in fetch
and using proxy in package.json
I removed JSON.stringify with the data and still it is null
See with postman I get the same string back
I even tried this
const data = {'firstname' : this.state.firstname};
it is still returned null
Finally it works. I had to encode the data being sent. Is there a better way to do this in Reactjs for more complex objects
const searchParams = Object.keys(data).map((key) => { return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]); }).join('&');
And then use searchParams in the body of fetch

Categories