1

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.

Dany
  • 2,692
  • 7
  • 44
  • 67

1 Answers1

1

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.

Community
  • 1
  • 1
icza
  • 389,944
  • 63
  • 907
  • 827
  • Thanks for all your suggestions. I am trying to implement the same. I will update the results here. – Dany Jul 15 '16 at 03:23
  • I implemented option 2. It worked like a charm. I have one question though. I am using config ajax call in script tag in home.html which imports another script view.js. I imported view.js in the footer of home.html. This view.js also makes config ajax call. I am doing this because of some other dependencies for view.js. But first script config ajax call is not working. My question is, will it be a problem if I make this config xhr request twice? I used the same code u suggested in option-2. – Dany Jul 15 '16 at 09:05
  • @Dany I would find out why first call doesn't work and fix that. But apart from that, you can make as many ajax calls as you want, you can repeat the same ajax call multiple times. As to how to use config that is requested from server: use a `configReceived` variable in Javascript which should be set to `true` if config call was successful, and do not use config variables until the above is `true`. – icza Jul 15 '16 at 09:44
  • ah ok. I really appreciate your help. – Dany Jul 15 '16 at 09:46
  • I am calling this config api in the header of the view.html(from external js file). I am also using some inline js scripts in view.html which uses the config endpoints. But unfortunately the inline scripts loads before the config external file call. I tried to do setTimeOut() with configReceived variable in while loop to not to load the inline content before config received. But that is blocking the thread and while loop never ends. Is there a way to do that? – Dany Jul 16 '16 at 04:11
  • @Dany Why don't you do it like when config call is successful, only then call further initialization process which depends on the config? Or use method #1 which doesn't require an AJAX call, because config is delivered right with the HTML file. – icza Jul 16 '16 at 05:20