How to get Nice-Looking URLS - Node.JS - javascript

I have been looking around the web for a way to get the URL that is like:
example.com/games/game1 instead of example.com/games?id=game1
I have looked around the Node.JS website but I couldn't find anything that seemed to apply to my situation.
Any help is very appreciated. I have found an answer that did this using a .HTACCESS file, but I couldn't find a node.js alternative. The question/answer that I found was, creating nice looking URLs
Any help is very appreciated.

This URL example.com/games?id=game1 is passing the id as a GET parameter. To replace it with example.com/games/game1, you just have to come with a strategy on how to pass this id. This strategy is usually referred to node.js as routes, and, they are plenty of options on how to achieve your goal:
If you are using Express framework, you have built in ability to do stuff like this (based off TJ Holowaychuk's route separation examples):
app.get('/games/:id', games.view);
Then, in your game.js file:
exports.view = function(req, res){
console.log(req.params.id); //gives you game1
//...
};
- Another way to do it is to use something specific for routing (instead of a whole framework). Director comes to mind.
var viewGame = function(gameId) { console.log(gameId); };
var routes = {
'/games/:gameId': viewGame
};

You can list to all requests, then parse request.url to decide which page to render or whether to return a 404 / 302 or whatever you want to do. This is just a small example. You probably want to separate your routing from your logic:
var http = require("http");
http.createServer(function(request, response) {
var parts = request.url.split('/');
if(parts[0] === 'games'){
var id = parts[1];
// Check if valid id
// And render the correct page
}
}).listen(80);

Related

Node.js - Extracting part of the URL

In my node.js server i havce a URL variable.
It is either in the form of "https://www.URL.com/USEFUL_PART/blabla", or "URL.com/USEFUL_PART/blabla".
From this, i want to extract only the 'USEFUL_PART' information.
How do i do that with Javascript?
I know there are two ways to do this, one with vanilla js and one with regular expressions.
I searched the web but i only found SO solutions to specific questions. Unfortunately, i coulnd't find a generic tutorial i could replicate or work out my solution.
Since you're using Express, you can specify the part of the URL you want as parameters, like so:
app.get('/:id/blabla', (req, res, next) => {
console.log(req.params); // Will be { id: 'some ID from the URL']
});
See also: https://expressjs.com/en/api.html#req.params
In Node.js you can use "URL"
https://nodejs.org/docs/latest/api/url.html
const myURL = new URL('https://example.org/abc/xyz?123');
console.log(myURL.pathname);
// Prints /abc/xyz
One way is to check whether the url starts with http or https, if not then manually add http, parse the url using the URL api, take the patname from parsed url, and get the desired part
let urlExtractor = (url) =>{
if(!/^https?:\/\//i.test(url)){
url = "http://" + url
}
let parsed = new URL(url)
return parsed.pathname.split('/')[1]
}
console.log(urlExtractor("https://www.URL.com/USEFUL_PART/blabla"))
console.log(urlExtractor("URL.com/USEFUL_PART/blabla"))
Hey if you are using express then you can do something like this
app.get('/users/:id/blabla',function(req, res, next){
console.log(req.params.id);
}
Another way is to use javascript replace and split function
str = str.replace("https://www.URL.com/", "");
str = str.split('/')[0];

JS: Node.js and Socket.io - globals and architecture

Dear all,
Im working with JS for some weeks and now I need a bit of clarification. I have read a lot of sources and a lot of Q&A also in here and this is what I learned so far.
Everything below is in connection with Node.js and Socket.io
Use of globals in Node.js "can" be done, but is not best practice, terms: DONT DO IT!
With Sockets, everything is treated per socket call, meaning there is hardly a memory of previous call. Call gets in, and gets served, so no "kept" variables.
Ok I build up some chat example, multiple users - all get served with broadcast but no private messages for example.
Fairly simple and fairly ok. But now I am stuck in my mind and cant wrap my head around.
Lets say:
I need to act on the request
Like a request: "To all users whose name is BRIAN"
In my head I imagined:
1.
Custom object USER - defined globally on Node.js
function User(socket) {
this.Name;
this.socket = socket; }
2.
Than hold an ARRAY of these globally
users = [];
and on newConnection, create a new User, pass on its socket and store in the array for further action with
users.push(new User(socket));
3.
And on a Socket.io request that wants to contact all BRIANs do something like
for (var i = 0; i < users.length; i++) {
if(user[i].Name == "BRIAN") {
// Emit to user[i].socket
}}
But after trying and erroring, debugging, googling and reading apparently this is NOT how something like this should be done and somehow I cant find the right way to do it, or at least see / understand it. can you please help me, point me into a good direction or propose a best practice here? That would be awesome :-)
Note:
I dont want to store the data in a DB (that is next step) I want to work on the fly.
Thank you very much for your inputs
Oliver
first of all, please don't put users in a global variable, better put it in a module and require it elsewhere whenever needed. you can do it like this:
users.js
var users = {
_list : {}
};
users.create = function(data){
this._list[data.id] = data;
}
users.get = function(user_id){
return this._list[user_id];
};
users.getAll = function(){
return this._list;
};
module.exports = users;
and somewhere where in your implementation
var users = require('users');
For your problem where you want to send to all users with name "BRIAN",
i can see that you can do this good in 2 ways.
First.
When user is connected to socketio server, let the user join a socketio room using his/her name.
so it will look like this:
var custom_namespace = io.of('/custom_namespace');
custom_namespace.on('connection', function(client_socket){
//assuming here is where you send data from frontend to server
client_socket.on('user_data', function(data){
//assuming you have sent a valid object with a parameter "name", let the client socket join the room
if(data != undefined){
client_socket.join(data.name); //here is the trick
}
});
});
now, if you want to send to all people with name "BRIAN", you can achieve it by doing this
io.of('/custom_namespace').broadcast.to('BRIAN').emit('some_event',some_data);
Second.
By saving the data on the module users and filter it using lodash library
sample code
var _lodash = require('lodash');
var Users = require('users');
var all_users = Users.getAll();
var socket_ids = [];
var users_with_name_brian = _lodash.filter(all_users, { name : "BRIAN" });
users_with_name_brian.forEach(function(user){
socket_ids.push(user.name);
});
now instead of emitting it one by one per iteration, you can do it like this in socketio
io.of('/custom_namespace').broadcast.to(socket_ids).emit('some_event',some_data);
Here is the link for lodash documentation
I hope this helps.

Ember - get target url of transition

I am creating an error hook in my Ember.js app to redirect you to the auth service if you are not allowed to view certain content (in other words, if the server returns a 401).
It looks like this:
Ember.Route = Ember.Route.extend({
error: function(error, transition){
if (error.status === 401) {
window.location.replace("https://auth.censored.co.za");
}
}
Our auth api works as follows: If you send it a parameter called target (which is a url), it will redirect you back to that target url after you've logged in.
So I want to somehow get the URL of the route the Ember app was trying to transition to.
Then my code will end up something like this
Ember.Route = Ember.Route.extend({
error: function(error, transition){
if (error.status === 401) {
var target = // Your answer here
window.location.replace("https://auth.censored.co.za?target=" + encodeURIComponent(target));
}
}
I came across a need for this too, and resorted to using some internal APIs. In my case, I wanted to reload the whole app so that if you're switching users there's not data left over from the other user. When I reload the app, I want to put the user at the URL they tried to transition to, but for which they had insufficient privileges. After they authenticate (and thus have the bearer token in localstorage) I wanted to use window.location.replace(url) to get a clean copy of the whole app with the user at the URL implied by the Ember Transition object. But the question was, how do I go from a Transition object to a URL? Here is my solution, which uses the generate method which is a private API of the router:
let paramsCount = 0;
let params = {};
let args = [transition.targetName];
// Iterate over route segments
for (let key1 in transition.params) {
if (transition.params.hasOwnProperty(key1)) {
let segment = transition.params[key1];
// Iterate over the params for the route segment.
for (let key2 in segment) {
if (segment.hasOwnProperty(key2)) {
if (segment[key2] != null) {
params[key2] = segment[key2];
paramsCount++;
}
}
}
}
}
if (paramsCount > 0) {
args.push(params);
}
let url = router.generate.apply(router, args);
You'll need to get the router somehow either with a container lookup or some other means. I got it by injecting the -routing service which is documented as an API that might be publically exposed in the future (used by link-to), and which happens to have the router as a property.
While messy, perhaps you might find this helpful.
I was able to use transition.intent.url to accomplish exactly this. I'm not sure if this is private or not -- relevant discussion: https://discuss.emberjs.com/t/getting-url-from-ember-router-transition-for-sso-login/7079/2.
After spending several hours searching for an answer to this question and using the Chrome debugger to try and reverse engineer the Ember 2.5 code, my conclusion is that what you are looking for is not possible at the present time.
For people who don't understand why someone wants to do this, the case arises when authentication (e.g the login page) is separated from the application. This is necessary if there is a requirement not to deliver any content (including the application itself) to the user if the user is not authenticated. In other words, the login page cannot be part of the application because the user is not allowed to access the application before logging in.
PS: I realize this is not a solution to the user's question and probably more suitable as a comment. However, I can't post comments.
Kevins answer is the most correct one, I came to a similar solution. Basically I found how the link-to component was populating the href attribute and used the same sort of code.
In your object inject -routing. I did so with:
'routing': Ember.inject.service('-routing'),
Then the code to generate the URL from the transition is as follows...
let routing = this.get('routing');
let params = Object.values(transition.params).filter(param => {
return Object.values(param).length;
});
let url = routing.generateURL(transition.targetName, params, transition.queryParams);

Express - Send a page AND custom data to the browser in a single request?

How simultaneously to render a page and transmit my custom data to browser. As i understood it needs to send two layers: first with template and second with JSON data. I want to handle this data by backbone.
As i understood from tutorials express and bb app interact as follows:
res.render send a page to browser
when document.ready trigger jQuery.get to app.get('/post')
app.get('/post', post.allPosts) send data to page
This is three steps and how to do it by one?
var visitCard = {
name: 'John Smit',
phone: '+78503569987'
};
exports.index = function(req, res, next){
res.render('index');
res.send({data: visitCard});
};
And how i should catch this variable on the page- document.card?
I created my own little middleware function that adds a helper method called renderWithData to the res object.
app.use(function (req, res, next) {
res.renderWithData = function (view, model, data) {
res.render(view, model, function (err, viewString) {
data.view = viewString;
res.json(data);
});
};
next();
});
It takes in the view name, the model for the view, and the custom data you want to send to the browser. It calls res.render but passes in a callback function. This instructs express to pass the compiled view markup to the callback as a string instead of immediately piping it into the response. Once I have the view string I add it onto the data object as data.view. Then I use res.json to send the data object to the browser complete with the compiled view :)
Edit:
One caveat with the above is that the request needs to be made with javascript so it can't be a full page request. You need an initial request to pull down the main page which contains the javascript that will make the ajax request.
This is great for situations where you're trying to change the browser URL and title when the user navigates to a new page via AJAX. You can send the new page's partial view back to the browser along with some data for the page title. Then your client-side script can put the partial view where it belongs on the page, update the page title bar, and update the URL if needed as well.
If you are wanting to send a fully complete HTML document to the browser along with some initial JavaScript data then you need to compile that JavaScript code into the view itself. It's definitely possible to do that but I've never found a way that doesn't involve some string magic.
For example:
// controller.js
var someData = { message: 'hi' };
res.render('someView', { data: JSON.stringify(someData) });
// someView.jade
script.
var someData = !{data};
Note: !{data} is used instead of #{data} because jade escapes HTML by default which would turn all the quotation marks into " placeholders.
It looks REALLY strange at first but it works. Basically you're taking a JS object on the server, turning it into a string, rendering that string into the compiled view and then sending it to the browser. When the document finally reaches the browser it should look like this:
// someSite.com/someView
<script type="text/javascript">
var someData = { "message": "hi" };
</script>
Hopefully that makes sense. If I was to re-create my original helper method to ease the pain of this second scenario then it would look something like this:
app.use(function (req, res, next) {
res.renderWithData = function (view, model, data) {
model.data = JSON.stringify(data);
res.render(view, model);
};
next();
});
All this one does is take your custom data object, stringifies it for you, adds it to the model for the view, then renders the view as normal. Now you can call res.renderWithData('someView', {}, { message: 'hi' });; you just have to make sure somewhere in your view you grab that data string and render it into a variable assignment statement.
html
head
title Some Page
script.
var data = !{data};
Not gonna lie, this whole thing feels kind of gross but if it saves you an extra trip to the server and that's what you're after then that's how you'll need to do it. Maybe someone can think of something a little more clever but I just don't see how else you'll get data to already be present in a full HTML document that is being rendered for the first time.
Edit2:
Here is a working example: https://c9.io/chevex/test
You need to have a (free) Cloud9 account in order to run the project. Sign in, open app.js, and click the green run button at the top.
My approach is to send a cookie with the information, and then use it from the client.
server.js
const visitCard = {
name: 'John Smit',
phone: '+78503569987'
};
router.get('/route', (req, res) => {
res.cookie('data', JSON.stringify(pollsObj));
res.render('index');
});
client.js
const getCookie = (name) => {
const value = "; " + document.cookie;
const parts = value.split("; " + name + "=");
if (parts.length === 2) return parts.pop().split(";").shift();
};
const deleteCookie = (name) => {
document.cookie = name + '=; max-age=0;';
};
const parseObjectFromCookie = (cookie) => {
const decodedCookie = decodeURIComponent(cookie);
return JSON.parse(decodedCookie);
};
window.onload = () => {
let dataCookie = getCookie('data');
deleteCookie('data');
if (dataCookie) {
const data = parseObjectFromCookie(dataCookie);
// work with data. `data` is equal to `visitCard` from the server
} else {
// handle data not found
}
Walkthrough
From the server, you send the cookie before rendering the page, so the cookie is available when the page is loaded.
Then, from the client, you get the cookie with the solution I found here and delete it. The content of the cookie is stored in our constant. If the cookie exists, you parse it as an object and use it. Note that inside the parseObjectFromCookie you first have to decode the content, and then parse the JSON to an object.
Notes:
If you're getting the data asynchronously, be careful to send the cookie before rendering. Otherwise, you will get an error because the res.render() ends the response. If the data fetching takes too long, you may use another solution that doesn't hold the rendering that long. An alternative could be to open a socket from the client and send the information that you were holding in the server. See here for that approach.
Probably data is not the best name for a cookie, as you could overwrite something. Use something more meaningful to your purpose.
I didn't find this solution anywhere else. I don't know if using cookies is not recommended for some reason I'm not aware of. I just thought it could work and checked it did, but I haven't used this in production.
Use res.send instead of res.render. It accepts raw data in any form: a string, an array, a plain old object, etc. If it's an object or array of objects, it will serialize it to JSON for you.
var visitCard = {
name: 'John Smit',
phone: '+78503569987'
};
exports.index = function(req, res, next){
res.send(visitCard};
};
Check out Steamer, a tiny module made for this this exact purpose.
https://github.com/rotundasoftware/steamer
Most elegant and simple way of doing this is by using rendering engine (at least for that page of concern). For example use ejs engine
node install ejs -s
On server.js:
let ejs = require('ejs');
app.set('view engine', 'ejs');
then rename desired index.html page into index.ejs and move it to the /views directory. After that you may make API endpoit for that page (by using mysql module):
app.get('/index/:id', function(req, res) {
db.query("SELECT * FROM products WHERE id = ?", [req.params.id], (error, results) => {
if (error) throw error;
res.render('index', { title: results[0] });
});
});
On the front-end you will need to make a GET request, for example with Axios or directly by clicking a link in template index.ejs page that is sending request:
<a v-bind:href="'/index/' + co.id">Click</a>
where co.id is Vue data parameter value 'co' that you want to send along with request

why should i declare handle["/"] at request handling object in http server with node.js?

I have encountered with this nice tutorial to node.js.
There is some example there :
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
server.start(router.route, handle);
Why do I need handle["/"] what is it good for?
he says there :
As you can see, it's really simple to map different URLs to the same request handler: by adding a key/value pair of "/" and requestHandlers.start, we can express in a nice and clean way that not only requests to /start, but also requests to / shall be handled by the start handler.
why that solve any problem and what is the problem at all?
The function binded to handle["/"] will be called when somebody visits yoursite.com, and handle["/start"] will be called for visits to yoursite.com/start
The author is trying to build a server that will display different things based on routes (urls).

Categories

Resources