How to customize public js files in Node.js by environment? - javascript

I'm trying to customize public javascript files based upon the environment. Specifically, for socket.io, I'm trying to customize the location the client will connect to:
development:
var socket = io.connect('http://localhost/chat');
production:
var socket = io.connect('http://xxx.xxx.xxx.xxx/chat');
I know all about environment variables within the app itself (and I do use node environment variables through express), but from what I can tell these variables won't touch the public static js files I'm serving to the client.
What's the best way to go about achieving this contextual switch based upon development/production environments?

If you're writing unobtrusive JavaScript and using a proper bundling system, then each HTML page you deliver should reference only a single "loader" script, which is then responsible for bringing in all the rest of the scripts. Create two versions of that script, one for development and one for production, and give them different names. When rendering HTML, make the name of the script a template variable which you'll have set based on your environment. Your loader script can then set appropriate variables to be used by the scripts it brings in.

The way I approached it was to load the socket.io in the layout page:
<script src='<%= socketIoUrl %>/socket.io/socket.io.js'></script>
<script type="text/javascript">
var socket = io.connect('<%= socketIoUrl %>');
</script>
Then I added a dynamic helper to expose the socketIoUrl:
var helpers = function(app) {
app.dynamicHelpers({
socketIoUrl: function(req, res) {
return app.settings.socketIoUrl;
}
});
};
module.exports = helpers;
And so in my server.js file I set the appropriate value based on the environment and loaded the helper file:
app.configure('development', function(){
app.set('socketIoUrl', 'http://localhost:3003');
});
app.configure('test', function(){
app.set('socketIoUrl', 'http://...');
});
app.configure('production', function(){
app.set('socketIoUrl', 'http://...');
});
require('./apps/helpers')(app);
So now you can use the socket variable created in your layout page in any other js file you have.

Related

How to change endpoints for different stages in javascript [client side]?

I think it might have asked before. But I couldn't find any results based on my search keywords.
In my use case I am configuring endpoints based on stage information [desktop, preprod, prod]. I am using golang for backend. In the backend I use the APP_STAGE environment variable to read the respective configs. For example,
fileName = APP_STAGE + "-endpoints.cfg"
And I export the variable before starting the server.
$export APP_STAGE="desktop"
And desktop-endpoints.cfg will look like this,
{
"featured" : {
"httpendpoint": {
"url": "http://localhost:8081/api/services/featured/"
}
},
"latest" : {
"httpendpoint": {
"url": "http://localhost:8081/api/services/latest/"
}
}
}
But how can I achieve this in client side [javascript]? I have files in the following structure,
app/
view.js
featured.js
home.js
Each of the file uses different endpoints to make ajax calls. How can read the stage config based on some variable [if not env variable] in javascript?
Could someone help me with this? I am fairly new to javascript.
Thanks.
JavaScript files are executed at client side, by the browser. The browser does not have access to the server config files, so it is the server's responsibility to read/get proper config values and make them available to the client.
There are multiple ways to deal with this. I will outline some possible solutions.
1. Include the correct endpoints in the HTML files
You may choose to include the correct endpoints in the HTML files that refer to the javascript files in which they would be used.
The HTML files would be templates and not static files, and you can use the html/template package to execute those templates to include the necessary URLs and everything else you need, and generate the final HTML that will be sent to the clients.
The HTML template may contain a <script> tag initializing certain JavaScript variables, which then can be used from the included JavaScript files.
Here's a simple example passing the featured httpendpoint.
HTML Template ("home.html"):
<html>
<head>
<script>
var featuredHttpEndpoint = "{{.FeaturedHttpEndpoint}}";
</script>
<script src="view.js"></script>
<script src="featured.js"></script>
<script src="home.js"></script>
</head>
<body>Your body</body>
</html>
And the handler that would serve this HTML template:
var homeTempl = template.Must(template.New("").ParseFiles("home.html"))
func homeHandler(w http.ResponseWriter, r *http.Request) {
m := map[string]interface{}{
// Insert the value of endpoint from your config
"FeaturedHttpEndpoint": "http://localhost:8081/api/services/featured/",
}
if err := homeTempl.Execute(w, m); err != nil {
// Handle error
}
}
Mapping the home handler e.g.:
http.HandleFunc("/index.html", homeHandler)
And make sure the home page is not cached, so the browser will always ask for a fresh copy in which your server can insert the actual config values.
2. Perform AJAX requests from the JavaScript files
You may choose to perform AJAX requests from the JavaScript files to query the necessary information. A simple example of this can be found in this question: Dynamically refresh a part of the template when a variable is updated golang
In a real-life example you would transmit all the config values that the client needs with one request (e.g. in JSON format), here I only transmit a single value.
Getting / sending only the featured httpendpoint:
In JavaScript:
var featuredHttpEndpoint = "";
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var config = JSON.parse(xhr.responseText);
featuredHttpEndpoint = config.FeaturedHttpEndpoint;
}
}
xhr.open("GET", "/config.json", true);
try {
xhr.send();
} catch (err) {
// handle error
}
And the Go handler providing the config values:
func configHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
m := map[string]interface{}{
// Insert the value of endpoint from your config
"FeaturedHttpEndpoint": "http://localhost:8081/api/services/featured/",
}
if err := json.NewEncoder(w).Encode(m); err != nil {
// Handle error
}
}
Which must be mapped to the path the client calls, e.g.:
http.HandleFunc("/config.json", configHandler)
3. Use Javascript files as templates
You may also choose to make your Javascript files templates, and when serving them, you may use a template engine to include endpoints based on your environment / APP_STAGE. See point #1 as an example to serving templates.
If you're going down this path, you have to take care of properly configuring caching of the Javascript files, as the browser will not ask you again for the js files if their caching info says they are not expired.

Dynamically get web socket URL

I have a Play 2.5 application that uses a web socket. In my Controller I establish it as
def socket = WebSocket.accept[JsValue, JsValue] { request =>
ActorFlow.actorRef(out => TroiWebSocket.props(db, out, comm.communicator, system))
}
And, it's accessed in my routes as
GET /push-notifications controllers.Application.socket
As, currently, my application is running locally, I can reference the socket in a javascript file using
var socket = new WebSocket("ws://localhost:9000/push-notifications");
However, I'm starting to move my stuff away from the localhost, and need a way to reference the url in my javascript file. This URL might change (and could be different depending on the development environment). So, how can I reference this URL dynamically? That is, how do I say
var socket = new Websocket(URL_OF_WEBSOCKET)
I thought of breaking it up in my config files and trying to do it that way, but I'm not so sure that would work.
Any and all help would be appreciated.
If you are using plain javascript. Declare a File config.js and define some global Object with some config data.
<html>
<head>
<script>
var config = {
"localWSUrl" : "ws://localhost:9000/socket",
"wsUrl" : "ws://serverurl.com:443/socket"
}
</script>
<script>
console.log(config.wsUrl);
</script>
</head>
<body>
</body>
</html>
For simplicity sake I wrote everything in one file. You would exclude the config part and import the file via the script tag's src attribute. And then you can reuse it where you need it.
If the URL to get main page of your application is the same or partially same to connect websocket, suppose:
Url app: myapp.com
Websocket url: myapp.com/push-notification
So you could do in your js file using window.location of js standard api
var tcp = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
var host = window.location.host;
var path = '/push-notification';
var ws = new WebSocket(tcp+host+path);
Something like that..
I hope It helps.

Accessing file with JS when files are a folder deeper than domain

We have a MVC site which uses subdomains. Not in the traditional sub.domain.com but instead we are using domain.com/sub. The source files all exist in the sub folders of each sub domain because each might have some slightly different things. This causes the Dev team to have to place JS directly into the razor pages so the razor code was able to update URLs like below.
var temp = $('div').load('#Url.Content("~/Images/Excel.png")');
Unfortunately using a code like below in a separate JS file tries loading from domain.com and not domain.com/sub
var temp = $('div').load('/Content/Templates/warning.html');
Theses add on to the domains and can change with clients. Is there a way to get the domain plus sub when the files are loaded like that in the JS without needing to place the code into the razor? I'd prefer a separation of concerns because we are loading scripts sometimes which aren't even used because of it.
what I always do when in similar situations is that I create a function in the main.js or whatever name your using for your shared js file, modify the URL in the function and use the function as the initiator:
in the main.js:
var loadFile = function(selector,path){
$(selector).load('/sub'+path);
}
and then whenever and wherever you wanna load a file:
var temp = loadFile('div','/Content/Templates/warning.html');
UPDATE
you can upgrade your loadFile function to let it know if it has to load from the root of the website if needed:
var loadFile = function(selector,path,loadFromRoot){
var root=(loadFromRoot) ? '' : '/sub';
$(selector).load(root+path);
}

How to reference documents in my website to be independent of the website location and usage location

I'm relatively new to client side development. I'm creating an angularJS directive which references a static html, in [root]/Static/template.html.
I guess the problem is not unique to angularJS.
Now I need this address to be root relative, so that it can be loaded regardless of where I use the directive. The problem is that I don't know where my site will be uploaded, so it might be put in www.mysite.com/ or might be www.mysite.com/system/
I also can't use relative path, as it will be sensitive to where I use the directive, so for instance if I use Static/template.html, it will be found by documents in the website root, but not in the inner folders.
What is the correct way to reference documents to be robust?
If your static folder is relative the place where your application is deployed, e.g.:
www.example.com/index.html
www.example.com/Static
www.example.com/root/index.html
www.example.com/root/Static
www.example.com/root/foobar-app/index.html
www.example.com/root/foobar-app/Static
Then you need to extract the base url and store it somewhere. window.location API could be used to do that.
E.g.:
<!-- index.html -->
<!-- should probably be placed into external script file -->
<script>
function extractPath(url) {
return url.match(/.*\//) // find all chars until the slash
}
var baseurl = window.location.origin + extractPath(window.location.pathname);
window.baseurl = baseurl; // store in global scope
</script>
This snippet shows the general idea. Now elsewhere in your code you can read the base url path to access static resources. E.g.:
var image_url = window.baseurl + "Static" + image_path;
In AngularJS you would normally store that variable in the main app controller. If you only have one factory to access static resources, you could consider storing the baseurl there.
URL that starts with / is the URL from the root.
So if you set /Static/template.html, you can access template.html from both paths(www.mysite.com/ and www.mysite.com/system/).

My relative URL path to my View breaks on Deployment

I have a very standard ASP MVC app that I use a little javascript to show a Partial View. In order to make that Javascript work I needed to hard code a path to the Partial which is different between Dev and Production.
Mainly, in Dev there is no App specification where as in Production there is. See here:
Production=var URL = '/WetWashRequest/wetWashRequests/GetDetails?WONumber=' + wo;
Dev = var URL = '/wetWashRequests/GetDetails?WONumber=' + wo;
What this means is that as I work on it locally I delete the first part and when I want to deploy I have to remember to re add it.
This seems so ridiculously flawed that I can only assume I am being ignorant and doing something wrong...
You can take advantage of UrlHelper to get the URLs, as long as you do it in view:
var URL = '#Url.Action("GetDetails")';
Obviously, it doesn't make sense to put all your JavaScript in view, so what I will normal do is set just this in my view, in a namespace var, and then reference it in my external JavaScript:
View
<script>
var MyApplication = MyApplication || {};
MyApplication.GetDetailsUrl = '#Url.Action("GetDetails")';
</script>
External JS
$.get(MyApplication.GetDetailsUrl, { WONumber: wo }, function (result) {
...
});

Categories

Resources