understanding node.js callback - javascript

I'm using urllib to make a request to a webpage and I'm trying to return it's headers like so:
var getHeaders = function(webpage){
var info = urllib.request(webpage, {}, function(err, data, res){
// console.log(res.headers); works fine and shows them
return res.headers; // I thought it should make the info variable have the headers information
});
return info;
}
Now when I try to get the headers like maybe set-cookie of a webpage I intended it to return the that from the website but it doesn't, so is there a way to return the headers or is it just not possible to do that?

In Node pretty much everything is done asynchronously, so you'll just need your function to be asynchronous.
var getHeaders = function (webpage, done) {
urllib.request(webpage, {}, function(err, data, res){
done(err, res.headers);
});
}
The traditional pattern is to use callbacks that return an error as the first argument (or a falsy value in case everything went well), and whatever you need to return afterwards.
Consuming the method is then very similar to what you had to do with the urllib thing.
getHeaders(webpage, function (err, headers) {
if (err) {
throw err; // or, you know, deal with it.
}
console.log(headers);
});

Related

Handling a success in JS

I am experimenting with t library, and am trying to install the functionality into my own search bar to practice (still a student).
Can anyone provide advice to the format this would be in??
#PascalLamers answer is the cleanest way to go about it, using the "await" pattern.
To see what this looks like with promise chaining:
app.get('/:url', async function (req, res) {
return wappalyzer
.init()
.then(function(){
return wappalyzer.open(decodeURIComponent(req.params.url))
})
.then(function(site){
return site.analyze()
})
.then(function(data){
return res.status(200).json(data);
})
.catch(function(err){
return res.status(500).json({ message : err.message })
})
}
Each then executes a single async operation, and on success passes its result to the next then() operation.
Each then() is only called upon the success of the previous then(). If an error occurs at any point, the catch() is executed and the function returns (in this case, returns an error 500).
Completely ignoring what wappalyzer actually is or does, I would suggest the following, since you are already providing an async function as callback to your route controller :
app.get('/:url', async function (req, res) {
try {
await wappalyzer.init();
const site = await wappalyzer.open(decodeURIComponent(req.query.url));
const data = await site.analyze();
// if you end up here, everything was successfull
return res.status(200).json(data);
} catch (ex) {
// if you end up here, something went wrong
return res.status(500).json({ message : ex.message });
}
});
The above doesn't really make much sense, since you are telling your backend to look for an url param but using a query parameter instead. I woudln't recommend to send an url as either, param or query. Use the request body instead :
// receive request in backend, with an endpoint that makes a bit more sense
// also using POST method, otherwise you won't be able to send a body
app.post('/search', async function (req, res) {
const url = req.body.url;
// do stuff
});
// sending request to backend, using axios for example
const respond = await axios.post('/search', { url : 'www.google.com' });
// or using fetch
const respond = await fetch('/search', {
method: 'post',
body: JSON.stringify({ url : 'www.google.com' });
Please be aware these are just pointers, nothing to copy & paste :) .
If you are using Express, I suggest reading the documentation again. I think you are misunderstanding how the router works : https://expressjs.com/de/guide/routing.html#route-parameters

How do I search for a data in the database with Node JS, Postgres, and dust js

I'm making a webpage with Node JS with dustjs and PostgreSQL. How do I make a search query in the html, so I can pass the value to the app.get
Do I need to use JQuery?
app.get('/teachers', function(req, res){
pool.connect(function(err, client, done){
if(err) {
return console.error("error", err);
}
client.query('SELECT * FROM teachers', function(err, result){
if(err){
return console.error('error running query', err)
}
res.render('teacherindex', {teachers: result.rows});
done();
});
});
});
app.get('/teachers/:str', (req,res)=>{
pool.connect((err, client, done) => {
if (err) throw err
client.query('SELECT * FROM teachers WHERE name = $1', [req.query.namesearch], (err, result) => {
done()
if (err) {
console.log(err.stack)
} else {
res.render('teacherindex', {teachers: result.rows});
}
})
})
})
This is my JQuery
$("#myBtn").click(function(){
var str = $("#myInput").val();
var url = '/teachers/'+str;
if(confirm('Search Record?')){
$.ajax({
url: url,
type: 'put',
success: function(result){
console.log('Searching');
window.location.href='/teachers';
},
error: function(err){
console.log(err);
}
});
}
});
My HTML
<input type="text" id="myInput" data-id="namesearch">
<button type="button" id="myBtn">Show Value</button>
Thank you!
FINAL ANSWER:
Ok so it turns out the issue you were having was something completely different. You are trying to use server side rendering for this, and I was showing you how to render the retrieved data on the client side.
I have forked, and updated your repo - which can be found at the link below..
Please review my changes and let me know if you have any questions.
Working repo: https://github.com/oze4/hanstanawi.github.io
Demo Video: https://raw.githubusercontent.com/oze4/hanstanawi.github.io/master/fake_uni_demo.mp4
EDIT:
I went ahead and built a repository to try and help you grasp these concepts. You can find the repo here - I tried to keep things as simple and understandable as possible, but let me know if you have any questions.
I had to make some minor changes to the paths, which I have commented explanations on the code in the repo.
I am using a "mock" database (just a JSON object in a different file) but the logic remains the same.
The index.js is the main entry point and contains all route data.
The index.html file is what gets sent to the user, and is the main HTML file, which contains the jQuery code.
If you download/fork/test out the code in that repo, open up your browsers developer tools, go to the network tab, and check out the differences.
Using req.params
Using req.query
ORIGINAL ANSWER:
So there are a couple of things wrong with your code and why you are unable to see the value of the textbox server side.
You are sending a PUT request but your server is expecting a GET request
You are looking for the value in req.query when you should be looking for it in req.params
You are looking for the incorrect variable name in your route (on top of using query when you should be using params) req.query.namesearch needs to be req.params.str
See here for more on req.query vs req.params
More detailed examples below.
In your route you are specifying app.get - in other words, you are expecting a GET request to be sent to your server.. but your are sending a PUT request..
If you were sending your AJAX to your server by using something like /teachers?str=someName then you would use req.query.str - or if you wanted to use namesearch you would do: /teachers?namesearch=someName and then to get the value: req.query.namesearch
If you send your AJAX to your server by using the something like /teachers/someName then you should be using req.params.str
// ||
// \/ Server is expecting a GET request
app.get('/teachers/:str', (req, res) => {
// GET THE CORRECT VALUE
let namesearch = req.params.str;
pool.connect((err, client, done) => {
// ... other code here
client.query(
'SELECT * FROM teachers WHERE name = $1',
// SPECIFY THE CORRECT VALUE
namesearch,
(err, result) => {
// ... other code here
})
})
});
But in your AJAX request, you are specifying PUT.. (should be GET)
By default, AJAX will send GET requests, so you really don't have to specify any type here, but I personally like to specify GET in type, just for the sake of brevity - just more succinct in my opinion.
Again, specifying GET in type is not needed since AJAX sends GET by default, specifying GET in type is a matter of preference.
$("#myBtn").click(function () {
// ... other code here
let textboxValue = $("#myTextbox").val();
let theURL = "/teachers/" + textboxValue;
// OR if you wanted to use `req.query.str` server side
// let theURL = "/teachers?str=" + textboxValue;
if (confirm('Search Record?')) {
$.ajax({
url: theURL,
// ||
// \/ You are sending a PUT request, not a GET request
type: 'put', // EITHER CHANGE THIS TO GET OR JUST REMOVE type
// ... other code here
});
}
});
It appears you are grabbing the value correctly from the textbox, you just need to make sure your server is accepting the same type that you are sending.

Need clarification on calling Meteor methods asynchronously

So i've been doing some reading and I think I have a general grasp on this subject but could use some insight from someone more experienced. I've been trying to write a simple RSS reader in Meteor and have been facing some issues with calling the Meteor method asynchronously. I currently define the method on the server(synchronously) and call it on the client(asynchronously). What I don't understand is that when I try to make the HTTP.call on the server, I return an undefined value passed to my client if I pass a callback into the request. But when I make the API request synchronously everything seems to work fine. Is this the normal behavior I should expect/the way I should be making the API call?
Meteor.methods({
getSubReddit(subreddit) {
this.unblock();
const url = 'http://www.reddit.com/r/' + subreddit + '/.rss';
const response = HTTP.get(url, {}, (err, res) => {
if(!err) {
//console.log(res.content);
return res;
} else {
return err;
}
});
}
});
Here's the method defined on the server side. Note that logging res.content shows that I'm actually getting the right content back from the call. I've tried reading some other answers on the topic and seen some things about using Future/wrapAsync, but I'm not sure I get it. Any help would be greatly appreciated!
The HTTP.get is doing async work, so callback passed to it will be called out of this meteor method call context.
To get desired result you should do it like this:
Meteor.methods({
getSubReddit(subreddit) {
// IMPORTANT: unblock methods call queue
this.unblock();
const url = 'http://www.reddit.com/r/' + subreddit + '/.rss';
const httpGetSync = Meteor.wrapAsync(HTTP.get);
try {
const response = httpGetSync(url, {});
//console.log(response.content);
return response.content;
} catch (err) {
// pass error to client
throw new Meteor.Error(...);
}
}
});

Issuing internal express request

I'm curious if there is any way to issue an internal request in express without going through all the actual overhead of a real request. An example probably shows the motivation better:
app.get("/pages/:page", funciton(req, res)
{
database_get(req.params.page, function(result)
{
// "Page" has an internal data reference, which we want to inline with the actual data:
request(result.user_href, function(user_response)
{
result.user = user.response.json;
res.send(result);
});
});
});
/// ....
app.get("/user/:name", function() ... );
So what we have here is a route whose data requires making another request to get further data. I'd like to access it by just doing something like app.go_get(user_href) instead of the heavy weight actual request. Now, I've asked around and the going strategy seems to be "split out your logic". However, it actually requires me to duplicate the logic, since the recursive data is referenced properly through URLs (as in the example above). So I end up having to do my own routing and duplicating routes everywhere.
Can you avoid the overhead of a real request? No. If you need the href from the first request in order to go to get a user object, you absolutely need to follow that link by making a second "real request."
If you have a database of users, you CAN avoid the request by including the user's ID on the page, and making a regular database call instead of following your own href.
Demo refactor on splitting out logic:
// Keep as little logic as possible in your routes:
app.get('/page/:page', function(req, res){
var pageId = req.params.page;
makePage(pageId, function(err, result){
if(err){ return res.send(500) }
res.send(result)
})
})
// Abstract anything with a bunch of callback hell:
function makePage(pageId, callback){
database_get(pageId, function(result) {
// Since it's only now you know where to get the user info, the second request is acceptable
// But abstract it:
getUserByHref(result.user_href, function(err, data){
if(err){return callback(err)};
result.user = data.json;
callback(null, result);
});
});
}
// Also abstract anything used more than once:
function getUserByHref(href, callback){
request(href, function(err, response, body){
if(response.statusCode != 200){
return callback(err);
}
var user = JSON.parse(body);
return callback(null, user);
})
}
// It sounds like you don't have local users
// If you did, you would abstract the database call, and use getUserById
function getUserById(id, callback){
db.fetch(id, function(err, data){
return callback(err, data);
})
}
I've made a dedicated middleware for this uest, see my detailed answer here: https://stackoverflow.com/a/59514893/133327

Meteor Compensate for Asynchronousity

I'm trying to make a method in Meteor which reads a file and outputs the result. Below is my code.
Server:
Meteor.methods({
retTemplate: function(templateName){
var fs = Npm.require('fs');
var ret;
fs.readFile("./../../../../../client/" + templateName + ".html",{encoding: 'utf8'}, function(err, data) {
if(err)
return "ERROR";
console.log(data);
return data;
});
}
});
Client:
Meteor.call('retTemplate', submitName, Meteor.user(), function(e, r){
console.log(r);
$('#editTempData').val(r);
});
The code on the server side works (the server-side console is updated with the content of the html file), but the method is returning undefined to the client. I believe this is due to Meteor's asynchronous functions. Is there a way around this? I've been fiddling around for a bit to no avail.
Thanks.
EDIT: I think it would work if there weren't a readFile method called, because that sets up a bit of a double-asynchronous call, which might be the problem, but I don't know how to fix it.
You can't return a value from an asynchronous method and then return it to the client like that. Here is an alternative (but not ideal) solution.
Create a new Meteor.Collection on the client/server and simply subscribe subscribe/publish this collection. Save the data returned from fs.readFile() into your collection, and your client will be automagically notified when this occurs.
I found the answer. I needed to use Futures.
Working server:
Meteor.methods({
retTemplate: function(templateName){
var Future = Npm.require('fibers/future');
var fut = new Future();
var fs = Npm.require('fs');
var ret;
fs.readFile("./../../../../../client/" + templateName + ".html",{encoding: 'utf8'}, function(err, data) {
if(err)
return "ERROR";
console.log(data);
fut['return'](data);
});
return fut.wait();
}
});

Categories

Resources