Node JS sending data via URL - javascript

Recently i started programming with Node JS and found it an amazing replacement for php . In php i used to send get requests with Data in the url .
Something like : http://sample.com/public.php?x=helloworld
How to perform something like this in Node JS or is there a better way to send data to node unlike using the url in the above case .
Also , I have noticed that in some cases like stackoverflow , queries are different and dont include the file name
like /public?= instead of /public.php?=
How is this achieved , i always thought this was something related to REST . Also , if you have the answer you might as well guide me if it could be done with Node and a few sources to learn could be of help too .

the most regular way to use REST api
req.query
// GET /search?q=foo+bar
req.query.q
// => "foo bar"
// GET /phone?order=desc&phone[color]=black&shoe[type]=apple
req.query.order
// => "desc"
req.query.phone.color
// => "black"
req.params
// GET /user/william
req.params.name
// => "william"
req.body(for form data)
// POST /login
req.body.username
// => "william"
req.body.password
// => "xxxxxx"

You'll probably be much better off using a pre-existing module as your web server. You can set one up manually, but you have to know about a lot of potential edge cases and really understand web servers. Most people in node use express. In node, as in any server-side language, you can pass data around in a few ways. The query string is one. You can also put some parameters directly in the url (like "/users/12" where 12 is a user id). Depending on the type of request, you can put data in the body of the request. You can also pass cookies. These are not node-specific. Explaining how express works in a post like this would be crazy, so I'll just give you a short example of a what a route handler matching your example route might look like:
var express = require('express');
var app = express();
app.get('/public', function(req, res, next) {
// Get the value from the query string. Express makes the query
// available as an object on the request parameter.
var x = req.query.x;
// Execute your main logic
doSomethingWithX(x);
// Send a response
res.status(200).json({ foo: 'bar' });
});

Related

Is it possible to link a random html site with node javascript?

Is it possible to link a random site with node.js, when I say that, Is it possible to link it with only a URL, if not then I'm guessing it's having the file.html inside the javascript directory. I really wanna know if it's possible because the html is not mine and I can't add the line of code to link it with js that goes something like (not 100% sure) <src = file.html>
I tried doing document = require('./page.html'); and ('./page') but it didn't work and when I removed the .html at the end of require it would say module not found
My keypoint is that the site shows player count on some servers, and I wanna get that number by linking it with js and then using it in some code which I have the code to (tested in inspect element console) but I don't know how to link it properly to JS.
If you wanna take a look at the site here it is: https://portal.srbultras.info/#servers
If you have any ideas how to link a stranger's html with js, i'd really appreciate to hear it!
You cannot require HTML files unless you use something like Webpack with html-loader, but even in this case you can only require local files. What you can do, however, is to send an HTTP Request to the website. This way you get the same HTML your browser receives whenever you open a webpage. After that you will have to parse the HTML in order to get the data you need. The jsdom package can be used for both steps:
const { JSDOM } = require('jsdom');
JSDOM.fromURL('https://portal.srbultras.info/')
.then(({ window: { document }}) => {
const servers = Array.from(
document.querySelectorAll('#servers tbody>tr')
).map(({ children }) => {
const name = children[3].textContent;
const [ip, port] = children[4]
.firstElementChild
.textContent
.split(':');
const [playersnum, maxplayers] = children[5]
.lastChild
.textContent
.split('/')
.map(n => Number.parseInt(n));
return { name, ip, port, playersnum, maxplayers };
});
console.log(servers);
/* Your code here */
});
However, grabbing the server information from a random website is not really what you want to do, because there is a way to get it directly from the servers. Counter Strike 1.6 servers seem to use the GoldSrc / Source Server Protocol that lets us retrieve information about the servers. You can read more about the protocol here, but we are just going to use the source-server-query package to send queries:
const query = require('source-server-query');
const servers = [
{ ip: '51.195.60.135', port: 27015 },
{ ip: '51.195.60.135', port: 27017 },
{ ip: '185.119.89.86', port: 27021 },
{ ip: '178.32.137.193', port: 27500 },
{ ip: '51.195.60.135', port: 27018 },
{ ip: '51.195.60.135', port: 27016 }
];
const timeout = 5000;
Promise.all(servers.map(server => {
return query
.info(server.ip, server.port, timeout)
.then(info => Object.assign(server, info))
.catch(console.error);
})).then(() => {
query.destroy();
console.log(servers);
/* Your code here */
});
Update
servers is just a normal JavaScript array consisting of objects that describe servers, and you can see its structure when it is logged into the console after the information has been received, so it should not be hard to work with. For example, you can access the playersnum property of the third server in the list by writing servers[2].playersnum. Or you can loop through all the servers and do something with each of them by using functions like map and forEach, or just a normal for loop.
But note that in order to use the data you get from the servers, you have to put your code in the callback function passed to the then method of Promise.all(...), i.e. where console.log(servers) is located. This has to do with the fact that it takes some time to get the responses from the servers, and for that reason server queries are normally asynchronous, meaning that the script continues execution even though it has not received the responses yet. So if you try to access the information in the global scope instead of the callback function, it is not going to be there just yet. You should read about JavaScript Promises if you want to understand how this works.
Another thing you may want to do is to filter out the servers that did not respond to the query. This can happen if a server is offline, for example. In the solution I have provided, such servers are still in the servers array, but they only have the ip and port properties they had originally. You could use filter in order to get rid of them. Do you see how? Tell me if you still need help.

Sending an array with axios.get as params is undefined

I am making a get request with additional params options, since I am using that request on a filter, so the params are filters for what to get back:
const res = await axios.get("http://localhots:3000/getsomedata", {
params: {
firstFilter: someObject,
secondFilter: [someOtherObject, someOtherObject]
}
});
The request goes through just fine, on the other end, when I console.log(req.query); I see the following:
{
firstFilter: 'someObject',
'secondFilter[]': ['{someOtherObject}', '{someOtherObject}'],
}
If I do req.query.firstFilter that works just fine, but req.query.secondFilter does not work and in order for me to get the data, I have to do it with req.query["secondFilter[]"], is there a way to avoid this and be able to get my array of data with req.query.secondFilter?
My workaround for now is to do:
const filter = {
firstFilter: req.query.firstFilter,
secondFilter: req.query["secondFilter[]"]
}
And it works of course, but I don't like it, I am for sure missing something.
Some tools for parsing query strings expect arrays of data to be encoded as array_name=1&array_name=2.
This could be a problem if you have one or more items because it might be an array or might be a string.
To avoid that problem PHP required arrays of data to be encoded as array_name[]=1&array_name[]=2 and would discard all but the last item if you left the [] out (so you'd always get a string).
A lot of client libraries that generated data for submission over HTTP decided to do so in a way that was compatible with PHP (largely because PHP was and is very common).
So you need to either:
Change the backend to be able to parse PHP style
Change your call to axios so it doesn't generate PHP style
Backend
The specifics depend what backend you are using, but it looks like you might be using Express.js.
See the settings.
You can turn on Extended (PHP-style) query parsing by setting it to "extended" (although that is the default)
const app = express()
app.set("query parser", "extended");
Frontend
The axios documentation says:
// `paramsSerializer` is an optional function in charge of serializing `params`
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
So you can override that
const res = await axios.get("http://localhots:3000/getsomedata", {
params: {
firstFilter: someObject,
secondFilter: [someOtherObject, someOtherObject]
},
paramsSerializer: (params) => Qs.stringify(params, {arrayFormat: 'repeat'})
});
My example requires the qs module
This has to do with params not being serialized correctly for HTTP GET method. Remember that GET has no "body" params similar to POST, it is a text URL.
For more information I refer to this answer, which provides more detailed info with code snippets.

How to get data from back end side, to use it in the browser side?

I am new to programming, and I heard that some guys on this website are quite angry, but please don't be. I am creating one web app, that has a web page and also makes som ecalculations and works with database (NeDB). I have an index.js
const selects = document.getElementsByClassName("sel");
const arr = ["Yura", "Nairi", "Mher", "Hayko"];
for (let el in selects) {
for (let key in arr) {
selects[el].innerHTML += `<option>${arr[key]}</option>`;
}
}
I have a function which fills the select elements with data from an array.
In other file named: getData.js:
var Datastore = require("nedb");
var users = new Datastore({ filename: "players" });
users.loadDatabase();
const names = [];
users.find({}, function (err, doc) {
for (let key in doc) {
names.push(doc[key].name);
}
});
I have some code that gets data from db and puts it in array. And I need that data to use in the index.js mentioned above, but the problem is that I don't know how to tranfer the data from getData.js to index.js. I have tried module.exports but it is not working, the browser console says that it can't recognize require keyword, I also can't get data directly in index.js because the browse can't recognize the code related to database.
You need to provide a server, which is connected to the Database.
Browser -> Server -> DB
Browser -> Server: Server provides endpoints where the Browser(Client) can fetch data from. https://expressjs.com/en/starter/hello-world.html
Server -> DB: gets the Data out of the Database and can do whatever it want with it. In your case the Data should get provided to the Client.
TODOs
Step 1: set up a server. For example with express.js (google it)
Step 2: learn how to fetch Data from the Browser(Client) AJAX GET are the keywords to google.
Step 3: setup a Database connection from you Server and get your data
Step 4: Do whatever you want with your data.
At first I thought it is a simple method, but them I researched a little bit and realized that I didn't have enough information about how it really works. Now I solved the problem, using promises and templete engine ejs. Thank you all for your time. I appreciate your help)

Express with JSON Data Control

I use lowDB dependency to control the JSON Data with Express and actually it works. But there is a bug and I cannot find how to solve it.
I create /create page to add information in JSON file and it contains 4 form and submit button.
And In express I code like this. each forms data will save it in variable and push with lowdb module.
router.post('/post', function (req, res) {
let pjName = req.body.projectName;
let pjURL = req.body.projectURL;
let pjtExplanation = req.body.projectExplanation;
let pjImgURL = req.body.projectImgURL;
console.log(pjName);
db.get('project').push({
name: pjName,
url: pjURL,
explanation: pjtExplanation,
imgurl: pjImgURL
}).write();
console.log(db.get('project'));
console.log(db.get('project').value());
res.redirect('/');
})
And it works well. But when I modify the JSON file myself (ex. reset the JSON file) and execute again. It shows the data that I reset before. I think in this app somewhere saves the all data and show save it in array again.
And When I shutdown the app in CMD and execute again, the Array is initialized.
As you may already know the lowdb persist the data into your secondary memory (hdd), and may return a promise depending on your environment when you call write method.As mentioned in the doc
Persists database using adapter.write (depending on the adapter, may return a promise).
So the data may be still getting write when you read them, so the old data is queried. Try this,
db.get('project').push({
name: pjName,
url: pjURL,
explanation: pjtExplanation,
imgurl: pjImgURL
}).write().then(() => {
console.log(db.get('project'));
console.log(db.get('project').value());
});

NodeJS Modulization

So, I was told that passing around the request and or response variable in nodeJS is "bad practice". But this means that most of your code has to be in the server.js file, making it cluttered and kind of ugly.
How can you modularize your nodejs server, passing around req/res appropriately and be able to organize your code into separate files?
For example, I would like to split my socket routing, .get and .post into different files, but still be able to use the callback parameters like so:
app.io.route("disconnect", function(req,res) { <--- these params
db.query("UPDATE player_data SET online=0 WHERE id="+mysql.escape(req.session.user));
req.io.broadcast("event", {msg:req.session.username+" has logged out!"});
app.io.broadcast("reloadXY");
});
As of right now they're all in one file and I don't like that.
I think what the person meant by 'passing around' was something like this (in plain express):
app.get('/kittens', function(req, res) {
db.doAthing(req);
updateSomethingElse(res);
upvoteThisAnswer(res);
});
That is, passing around the two variables beyond the first function. This is bad because it becomes increasingly difficult to figure out where the call actually ends. One little res.end(500) in updateSomethingElse can cause the whole house of cards to come tumbling down.
It's perfectly ok (in fact, standard to the point of being the default in express) to declare that callback elsewhere (usually the /routes directory of your project.)
// app.js
var user = require('./routes/user')
, kittens = require('./routes/kittens');
// express stuff...
app.get('/settings', user.getSettings);
app.get('/fur', kittens.shed);
Then, in routes/user.js:
exports.getSettings = function(req, res) {
// Note how we're passing around properties of req/res, not the objects themselves.
db.getUserSettings(req.user.id).then(function(settings) {
res.render('settings', settings);
});
};
This video from TJ Holowaychuk (the guy who wrote Express and a ton of other Node infrastructure that we all use) helped me take Express modularization to the next level. Basically you can make individual apps in their own folders and consume them as middleware very easily. I have managed to extend this technique to socket.io with some tricks.
http://vimeo.com/56166857
You should not pass req and res to another modules but pass callbacks from another modules to route.
It should look like.
var someModule = require("./someModule")
app.get("/someAction", someModule.handleSomeAction) ;
If You want to have post and get in another modules You should pass reference to app (from express()) once to that module and operate on that.
For example :
var express = require("express") ;
var app = express();
var get_handler = require("./get_handler ")
var post_handler = require("./post_handler ")
get_handler.init(app);
post_handler.init(app);
and in post/get_handler :
var app;
exports.init = function( eApp){
app = eApp;
// operate on app
}

Categories