Fetch client having an issue with JSON array assignment - json

The Aurelia fetch client docs have a basic example of getting json data:
bind() {
let client = new HttpClient();
return client.fetch('data.json')
.then(response => response.json())
.then(data => {
console.log(data[1]);
});
}
The above works fine yet the following does not:
files = [];
bind() {
let client = new HttpClient();
return client.fetch('data.json')
.then(response => response.json())
.then(files => this.files = files);
}
Gulp now complains "error TS2322: Type 'Response' is not assignable to type 'any[]'."
Even more odd is that I now get XHR 404 errors in my console. This makes no sense; the data.json file had no issue being found and fetched the first time. The only difference in the second code snippet is that instead of logging the data to the console, I'm actually trying to do something with it.

I believe your specific issue may be caused by an older version of TypeScript (2.1, the latest is 2.5). If you have an opportunity to do so, you can try updating it.
response in the statement response => is of type Response defined by Aurelia. When you are running this.files = files, it seems like TypeScript thinks that files is of type Response. You are already implicitly declaring this.files as type any[], so the assignment is not allowed.
You can get around this by setting an explicit type for files, or even just using any:
.then((files: any[]) => this.files = files);
I would try to avoid using any to get around type safety and work with the types, but the issue you're running into appears to be a bug in the version of TypeScript and/or Aurelia that you're using.

Related

Angular: Observable with subscribe returns error 200 OK as the response is not JSON

I am developing a front-end web application using Angular 11. This application uses several services which return data in JSON format.
I use the async / await javascript constructs and the Observables to get the answers from these services. This is an example my call:
let myResponse = await this.myService(this.myData);
myResponse.subscribe(
res => {
console.log("Res: ",res)
}, (error) => {
console.log("Error: ",error)
}
);
where this.myService contains the code doing the HTTP call using Angular httpClient.
Unfortunately a specific service (only one!) doesn't return data in JSON format but it returns a byte array (string that identifies a pdf -format application/pdf-).
Unfortunately this invocation causes a very strange error with code 200 OK:
How can I do to prevent res from being interpreted as JSON and therefore this error being reported? How can I read resreporting that it will not be in json format?
This service has no errors (with Postman it works perfectly). The problem is Javascript and Observable which are interpreted as JSON. How can I read the content of res in this case?
If a HTTP API call is not returning a JSON, just provide the proper value in the responseType option:
this.httpClient.get('<URL>', {
responseType: 'arraybuffer'
});
Ref: https://angular.io/api/common/http/HttpClient#description

How to get around previously declared json body-parser in Nodebb?

I am writing a private plugin for nodebb (open forum software). In the nodebb's webserver.js file there is a line that seems to be hogging all incoming json data.
app.use(bodyParser.json(jsonOpts));
I am trying to convert all incoming json data for one of my end-points into raw data. However the challenge is I cannot remove or modify the line above.
The following code works ONLY if I temporarily remove the line above.
var rawBodySaver = function (req, res, buf, encoding) {
if (buf && buf.length) {
req.rawBody = buf.toString(encoding || 'utf8');
}
}
app.use(bodyParser.json({ verify: rawBodySaver }));
However as soon as I put the app.use(bodyParser.json(jsonOpts)); middleware back into the webserver.js file it stops working. So it seems like body-parser only processes the first parser that matches the incoming data type and then skips all the rest?
How can I get around that? I could not find any information in their official documentation.
Any help is greatly appreciated.
** Update **
The problem I am trying to solve is to correctly handle an incoming stripe webhook event. In the official stripe documentation they suggested I do the following:
// Match the raw body to content type application/json
app.post('/webhook', bodyParser.raw({type: 'application/json'}),
(request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig,
endpointSecret);
} catch (err) {
return response.status(400).send(Webhook Error:
${err.message});
}
Both methods, the original at the top of this post and the official stripe recommended way, construct the stripe event correctly but only if I remove the middleware in webserver. So my understanding now is that you cannot have multiple middleware to handle the same incoming data. I don't have much wiggle room when it comes to the first middleware except for being able to modify the argument (jsonOpts) that is being passed to it and comes from a .json file. I tried adding a verify field but I couldn't figure out how to add a function as its value. I hope this makes sense and sorry for not stating what problem I am trying to solve initially.
The only solution I can find without modifying the NodeBB code is to insert your middleware in a convenient hook (that will be later than you want) and then hack into the layer list in the app router to move that middleware earlier in the app layer list to get it in front of the things you want to be in front of.
This is a hack so if Express changes their internal implementation at some future time, then this could break. But, if they ever changed this part of the implementation, it would likely only be in a major revision (as in Express 4 ==> Express 5) and you could just adapt the code to fit the new scheme or perhaps NodeBB will have given you an appropriate hook by then.
The basic concept is as follows:
Get the router you need to modify. It appears it's the app router you want for NodeBB.
Insert your middleware/route as you normally would to allow Express to do all the normal setup for your middleware/route and insert it in the internal Layer list in the app router.
Then, reach into the list, take it off the end of the list (where it was just added) and insert it earlier in the list.
Figure out where to put it earlier in the list. You probably don't want it at the very start of the list because that would put it after some helpful system middleware that makes things like query parameter parsing work. So, the code looks for the first middleware that has a name we don't recognize from the built-in names we know and insert it right after that.
Here's the code for a function to insert your middleware.
function getAppRouter(app) {
// History:
// Express 4.x throws when accessing app.router and the router is on app._router
// But, the router is lazy initialized with app.lazyrouter()
// Express 5.x again supports app.router
// And, it handles the lazy construction of the router for you
let router;
try {
router = app.router; // Works for Express 5.x, Express 4.x will throw when accessing
} catch(e) {}
if (!router) {
// Express 4.x
if (typeof app.lazyrouter === "function") {
// make sure router has been created
app.lazyrouter();
}
router = app._router;
}
if (!router) {
throw new Error("Couldn't find app router");
}
return router;
}
// insert a method on the app router near the front of the list
function insertAppMethod(app, method, path, fn) {
let router = getAppRouter(app);
let stack = router.stack;
// allow function to be called with no path
// as insertAppMethod(app, metod, fn);
if (typeof path === "function") {
fn = path;
path = null;
}
// add the handler to the end of the list
if (path) {
app[method](path, fn);
} else {
app[method](fn);
}
// now remove it from the stack
let layerObj = stack.pop();
// now insert it near the front of the stack,
// but after a couple pre-built middleware's installed by Express itself
let skips = new Set(["query", "expressInit"]);
for (let i = 0; i < stack.length; i++) {
if (!skips.has(stack[i].name)) {
// insert it here before this item
stack.splice(i, 0, layerObj);
break;
}
}
}
You would then use this to insert your method like this from any NodeBB hook that provides you the app object sometime during startup. It will create your /webhook route handler and then insert it earlier in the layer list (before the other body-parser middleware).
let rawMiddleware = bodyParser.raw({type: 'application/json'});
insertAppMethod(app, 'post', '/webhook', (request, response, next) => {
rawMiddleware(request, response, (err) => {
if (err) {
next(err);
return;
}
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
// you need to either call next() or send a response here
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
});
});
The bodyParser.json() middleware does the following:
Check the response type of an incoming request to see if it is application/json.
If it is that type, then read the body from the incoming stream to get all the data from the stream.
When it has all the data from the stream, parse it as JSON and put the result into req.body so follow-on request handlers can access the already-read and already-parsed data there.
Because it reads the data from the stream, there is no longer any more data in the stream. Unless it saves the raw data somewhere (I haven't looked to see if it does), then the original RAW data is gone - it's been read from the stream already. This is why you can't have multiple different middleware all trying to process the same request body. Whichever one goes first reads the data from the incoming stream and then the original data is no longer there in the stream.
To help you find a solution, we need to know what end-problem you're really trying to solve? You will not be able to have two middlewares both looking for the same content-type and both reading the request body. You could replace bodyParser.json() that does both what it does now and does something else for your purpose in the same middleware, but not in separate middleware.

ExpressJS - What happens when .json() is used in with a response?

I was making a server using ExpressJS and this thing is really bugging me. What is the use of using .json() instead of .send() with my responses. According to Express, .send() automatically coverts the JavaScript objects into JSON strings and we do not need to use stringify.Then why use .json() with my responses.
app.get('/profile/:id', (req, res) => {
const {id} = req.params;
let found = false;
database.users.forEach(user => {
if(user.id === id) {
found = true;
return res.json(user);
}
})
if (found === false)
{
res.status(400).json('not found');
}
})
Here is the code where res.json() apppears.
well in the end res.json calls res.send, and they both are identical when you pass an object or array.
But res.json method also uses json replacer and json spaces settings. They give you more flexibility and options to format your json file.
also res.json makes sure the response is in utf8 charset.
To better understand the differences I recommend you to check :
Difference between res.send and res.json in Express.js

React.js: setState method setting variable to a string instead of object... is there a workaround?

I am trying to fetch a simple JSON element from express.js. I am trying have React assign it to a state variable on the front end. I am using this code to do so:
componentDidMount() {
fetch("/user")
.then(response => response.json())
.then(result => this.setState({myUser:result}))
}
But when I run typeof myUser after this setState command, it says string instead of object. I've tried using JSON.parse(), etc. But either I get an error or it continues to assign the data as a string rather than JSON. What sort of syntax do I need to use in this fetch-then context to coerce the data assignment to be JSON?
I have read this link:
With this code:
componentDidMount(){
fetch('https://abx.com/data/tool.json').then(response =>{
if (!response.ok) throw Error('Response not ok')
return response.json(); // This is built in JSON.parse wrapped as a Promise
}).then(json => {
this.setState({"sections" : json});
}).catch(err =>{
console.log(err);
});
}
But it doesn't solve the problem. I ran this code directly in my application verbatim. When I run typeof on the variable, it says string instead of object. I looked at other posts on Stack Overflow, but I did not see a solution to this.
I figured out what was going wrong (after many hours of experimenting):
On the server side, I was creating a "homegrown" JSON object using string and variable concatenation. I also tried creating the JSON object by doing this:
var str = "name:" + name + ", department:" + department
var user = {str};
Both of these were not working in subtle ways... despite trying different types of gadgetry on the client side, I couldn't get React to interpret the data as a JSON object. But then I had an idea to construct the JSON on the server side (in Express.js) like this:
var user = {};
user["name"] = name;
user["department"] = department;
That immediately cleared things up on the server side and the client side. When using setState() in React, it now sets the value as an object (which was the goal all along).
I think this can be useful to others... if React doesn't seem to understand the JSON, perhaps it is being sent from the server in a subtly incorrect format.

read JSON using angular2

I have problem with json\object I'M trying to pull the data out from it and I failed.
I have this API that I pull my data from:
http://api.fixer.io/latest?base=CAD
I have placed it on variable results,
If i want to get to object paramater date,base,rate like below:
calc(details) {
let results = [this.networkServices.getCurrency(details)]; // object is here "getCurrency deal with the GET request.
alert(results.base);
}
I get the error code:
[02:58:36] transpile update started ...
[02:58:38] typescript: D:/ionic/firstApp/src/pages/currency/currency.ts, line: 19
Property 'base' does not exist on type 'Promise<any>[]'.
L18: let results = [this.networkServices.getCurrency(details)];
L19: alert(results.base);
[02:58:38] transpile update failed
Its feel weird that I can't pull the data out, what could it be?
get currency function
getCurrency(obj){
console.log("function fired!")
let url = `http://api.fixer.io/latest?base=${obj.selectedCurrency}`;
return this.http.get(url).toPromise().then(res => res.json());
}
Try updating your getCurrency() to just return the promise by removing the then():
getCurrency(obj){
console.log("function fired!")
let url = `http://api.fixer.io/latest?base=${obj.selectedCurrency}`;
return this.http.get(url).toPromise();
}
Then the solution from #pe8ter should work:
this.networkServices.getCurrency(details).then(result => alert(result))
The service request is asynchronous so the result of the request is a Promise that resolves to an object, not the object itself. Try something like this:
this.networkServices.getCurrency(details).then(result => alert(result))