Understanding anonymous functions in Express js - javascript

I am new to express and am trying to wrap my head around callbacks in RESTful actions. In my PUT request below, I'm confused about the following line that I have bolded below. Why is response.pageInfo.book being set to the second parameter in the anonymous function (result)? that seems kind of arbitrary.
Also, what is the best way to inspect some of these parameters (req, res, result, etc)? When I console.log it, doesn't show up in my terminal or in my browser console.
exports.BookEdit = function(request, response) {
var id = request.params.id;
Model.BookModel.findOne({
_id: id
}, function(error, result) {
if (error) {
console.log("error");
response.redirect('/books?error=true&message=There was an error finding a book with this id');
} else {
response.pageInfo.title = "Edit Book";
**response.pageInfo.book = result;**
response.render('books/BookEdit', response.pageInfo)
}
})
}

The findOne function takes a query ({_id : id}) and a callback as arguments. The callback gets called after findOne has finished querying the database. This callback pattern is very common in nodejs. Typically the callback will have 2 arguments
the first one error is only set if there was an error.
the second one usually contains the value being returned. In this case you are finding one book in the database.
The line you have bolded is where the book object is assigned to a variable which will be sent back to be rendered in the browser. It is basically some javascript object.
Your second request, to debug this stuff, here is what you can do:
In you code type the word debugger;
e.g.
var id = request.params.id;
debugger;
Next, instead of running your program like this:
node myprogram.js
... run with debug flag, i.e.
node debug myprogram.js
It will pause at the beginning and you can continue by pressing c then Enter
Next it will stop at that debugger line above. Type repl and then Enter and you'll be able to inspect objects and variables by typing their names.
This works very well and requires no installation. However, you can also take a more visual approach and install a debugger such as node-inspector which does the same thing but in a web browser. If you use a good IDE (e.g. webstorm) you can also debug node.js pretty easily.

In the above, the document that is the result of the findOne() query is being added to the pageInfo key of the response and is then being rendered in a template. The first parameter is a potential error that must be checked and the remainder contain data. It's the standard node idiom that an asynchronous call returns to a callback where you do your work.
The writer of the code has also decided to decorate the response object with an extra attribute. This is often done when a request passes through a number of middleware functions and you might want to build up the response incrementally (for example having a middleware function that adds information about the current user to the pageInfo key).
Look and see what else is on response.pageInfo. Information was probably put there by previous middleware (especially since the function above expects the pageInfo key to exist). Just do a console.log(response.pageInfo) and look on your server log or standard out.

Related

Unable to catch error when fetching data in async function

I'm using npm yahoo-finance to fetch stock data. When I input a stock symbol that doesn't exist, I would like to catch the error.
const yahooFinance = require('yahoo-finance');
async function stockData() {
try {
let data = await yahooFinance.historical({symbol: "SIJGAOWSFA", from: 2020-08-23, to: 2021-08-23});
} catch (error) {
console.error(error)
}
}
stockData();
However it doesn't appear to be a typical fetch error. It's not being caught at all. By that I mean, the error you see below was not logged to the console via the console.error(error). Rather something outside the scope of this file is logging the error. When the error occurs, nothing in catch is executed.
I plan on using this in a for loop, so would like to catch the error so I can avoid executing any following functions.
A collaborator says that:
Is this from an existing project that was working and stopped working, or a new project?
If the former - everything is still working fine on my side. (Very) occasionally there are issues at yahoo that get stuck in their cache, possibly relating to DNS too. I'd suggest to clear your DNS cache and also try querying different data to see if that works.
If the latter (new project), it could be the data you're querying. Try query different data and see if it works. Usually yahoo throws back a specific error if something wrong, but it could be this.
If neither of those approaches work, but you still need to catch this sort of error, given the source code, what it does is:
if (!crumb) {
console.warn('root.Api.main context.dispatcher.stores.CrumbStore.crumb ' +
'structure no longer exists, please open an issue.');
And then continues on as normal (without throwing), and eventually returns an empty array.
If you're sure the result should contain at least one item, you can check to see if it's empty, and enter into an error state if it is.
Otherwise, if you don't know whether the array should contain values or not, another option is to overwrite console.warn so that you can detect when that exact string is passed to it.
Another option would be to fork the library so that it (properly) throws an error when not found, instead of continuing on and returning an empty array, making an empty successful result indistinguishable from an errored empty result. Change the
if (!crumb) {
console.warn('root.Api.main context.dispatcher.stores.CrumbStore.crumb ' +
'structure no longer exists, please open an issue.');
to
if (!crumb) {
throw new Error('root.Api.main context.dispatcher.stores.CrumbStore.crumb ' +
'structure no longer exists, please open an issue.');
and then you'll be able to catch it in your call to .historical.

matrix-js-sdk setup and configuration

I am having some issues trying to connect to a matrix server using the matrix-js-sdk in a react app.
I have provided a simple code example below, and made sure that credentials are valid (login works) and that the environment variable containing the URL for the matrix client is set. I have signed into element in a browser and created two rooms for testing purposes, and was expecting these two rooms would be returned from matrixClient.getRooms(). However, this simply returns an empty array. With some further testing it seems like the asynchronous functions provided for fetching room, member and group ID's only, works as expected.
According to https://matrix.org/docs/guides/usage-of-the-matrix-js-sd these should be valid steps for setting up the matrix-js-sdk, however the sync is never executed either.
const matrixClient = sdk.createClient(
process.env.REACT_APP_MATRIX_CLIENT_URL!
);
await matrixClient.long("m.login.password", credentials);
matrixClient.once('sync', () => {
debugger; // Never hit
}
for (const room of matrixClient.getRooms()) {
debugger; // Never hit
}
I did manage to use the roomId's returned from await matrixClient.roomInitialSync(roomId, limit, callback), however this lead me to another issue where I can't figure out how to decrypt messages, as the events containing the messages sent in the room seems to be of type 'm.room.encrypted' instead of 'm.room.message'.
Does anyone have any good examples of working implementations for the matrix-js-sdk, or any other good resources for properly understanding how to put this all together? I need to be able to load rooms, persons, messages etc. and display these respectively in a ReactJS application.
It turns out I simply forgot to run startClient on the matrix client, resulting in it not fetching any data.

Call stack size exceeded on re-starting Node function

I'm trying to overcome Call stack size exceeded error but with no luck,
Goal is to re-run the GET request as long as I get music in type field.
//tech: node.js + mongoose
//import components
const https = require('https');
const options = new URL('https://www.boredapi.com/api/activity');
//obtain data using GET
https.get(options, (response) => {
//console.log('statusCode:', response.statusCode);
//console.log('headers:', response.headers);
response.on('data', (data) => {
//process.stdout.write(data);
apiResult = JSON.parse(data);
apiResultType = apiResult.type;
returnDataOutside(data);
});
})
.on('error', (error) => {
console.error(error);
});
function returnDataOutside(data){
apiResultType;
if (apiResultType == 'music') {
console.log(apiResult);
} else {
returnDataOutside(data);
console.log(apiResult); //Maximum call stack size exceeded
};
};
Your function returnDataOutside() is calling itself recursively. If it doesn't gets an apiResultType of 'music' on the first time, then it just keeps calling itself deeper and deeper until the stack overflows with no chance of ever getting the music type because you're just calling it with the same data over and over.
It appears that you want to rerun the GET request when you don't have music type, but your code is not doing that - it's just calling your response function over and over. So, instead, you need to put the code that makes the GET request into a function and call that new function that actually makes a fresh GET request when the apiResultType isn't what you want.
In addition, you shouldn't code something like this that keeping going forever hammering some server. You should have either a maximum number of times you try or a timer back-off or both.
And, you can't just assume that response.on('data', ...) contains a perfectly formed piece of JSON. If the data is anything but very small, then the data may arrive in any arbitrary sized chunks. It make take multiple data events to get your entire payload. And, this may work on fast networks, but not on slow networks or through some proxies, but not others. Instead, you have to accumulate the data from the entire response (all the data events that occur) concatenated together and then process that final result on the end event.
While, you can code the plain https.get() to collect all the results for you (there's an example of that right in the doc here), it's a lot easier to just use a higher level library that brings support for a bunch of useful things.
My favorite library to use in this regard is got(), but there's a list of alternatives here and you can find the one you like. Not only do these libraries accumulate the entire request for you with you writing any extra code, but they are promise-based which makes the asynchronous coding easier and they also automatically check status code results for you, follow redirects, etc... - many things you would want an http request library to "just handle" for you.

How to create a Shared Query Folder using the vso-node-api (VSTS)?

In the VSTS Rest API, there's a piece of documentation showing me how to create a folder. Specifically, I would like to create a folder within the Shared Queries folder. It seems like I can do this with the REST API.
I would like to do the same thing with the VSTS Node API (vso-node-api). The closest analogous function I can seem to find would be WorkItemTrackingApi.createQuery. Is this the correct function to use?
When I try to use this function, I'm getting an error:
Failed request: (405)
That seems strange, since a "Method Not Allowed" error doesn't seem like the right error here. In other words, I'm not the person deciding what method (GET/POST/...etc) to use, I'm just calling the VSTS Node API's function which should be using the correct HTTP Request Method.
I think the error code would/should be different if something about my request is wrong (like providing bad parameters/data).
But, I would not be surprised if VSTS didn't like the data I provided with the request. I wrote the following test function:
async function createQueryFolder (QueryHeirarchyItem, projectId, query) {
let result = await (WorkItemTrackingApi.createQuery(QueryHeirarchyItem, projectId, query))
return result
}
I set some variables and called the function:
let projectID = properties.project // A previously set project ID that works in other API calls
let QueryHeirarchyItem = {
isFolder: true,
name: 'Test Shared Query Folder 1'
}
try {
let result = await createQueryFolder(QueryHeirarchyFunction, projectID, '')
Notice that I provided a blank string for the query - I have no idea what to provide there when all I want to create is a folder.
So, I think a lot of things could be wrong with my approach here, but also if my request parameters are wrong maybe I should be getting a 400 error? 405 leads me to believe that the VSTS Node API is making a REST call that the underlying VSTS REST API doesn't understand.
For the third parameter of the createQueryFolder, you should specify the folder path where you want to create the new folder.
Such as if you want to create a folder Test Shared Query Folder 1 under Shared Queries, you should specify parameters for createQueryFolder as:
let result = await createQueryFolder(QueryHeirarchyFunction, projectID, 'Shared Queries')

Node.js | loop over an array , make post calls and accumulate result in an array

I wish to make a call in Node.js somethine like this (i m using coffeescript for node.js)
test = [] //initially an empty array
list = []//an array with 10 json object
for li in list
get_data url , li, (err,data) -> test.push data
my get_data method look like
get_data: (url, json_data, callback) ->
throw "JSON obj is required" unless _.isObject(json_data)
post_callback = (error, response) ->
if error
callback(error)
else
callback(undefined, response)
return
request.post {url: url, json: json_data}, post_callback
return
problem is I am not able to collect the result from request.post into the 'test' array
I Know I am doing something wrong in the for loop but not sure what
You don't appear to have any way of knowing when all of the requests have returned. You should really consider using a good async library, but here's how you can do it:
test = [] //initially an empty array
list = []//an array with 10 json object
on_complete = ->
//here, test should be full
console.log test
return
remaining = list.length
for li in list
get_data url , li, (err,data) ->
remaining--
test.push data
if remaining == 0
on_complete()
In just looking at your code (not trying it out), the problem seems to be a matter of "when you'll get the response" rather than a matter of "if you'll get the response". After your for loop runs, all you have done is queue a bunch of requests. You need to either design it so the request for the second one doesn't occur until the first has responded OR (better) you need a way to accumulate the responses and know when all of the responses have come back (or timed out) and then use a different callback to return control to the main part of your program.
BTW, here is code for a multi-file loader that I created for ActionScript. Since I/O is asynchronous in ActionScript also, it implements the accumulating approach I describe above. It uses events rather than callbacks but it might give you some ideas on how to implement this for CoffeeScript.

Categories

Resources