How to inject variables to Javascript based on environment? - javascript

I'm building a javascript application and basically I have three different environments. Local, CI and QA environments. My back-end service is always going to be deployed separately to my front-end application. So, most of the Ajax calls are going to be CORS. We solved this problem already. However, in my code there's something like this.
jQuery.ajax({"url":"http://www.somecompany.com/api"});
So the problem is going to be in CI and QA environments which ajax calls will hit http://ci.somecompany.com/api and http://qa.somecompany.com/api or even http://test.somecompany.com/api. How do I inject those variables based on the environment I'm running. If it's Java or .NET I could use YAML and everything would be ok. But as Javascript it's client-side how do we choose which url to hit based on environments without find and replace in the code or some hacky way. What do people do to overcome this kind of problem generally?
Just a side note, I'm using Knockout.js

If you build your application with grunt then use grunt-variablize. Put your configuration into resources/config.json
{
"test": {
"apiUrl": "http://test.somecompany.com/api"
},
"prod": {
"apiUrl": "http://www.somecompany.com/api"
}
}
configure variablize task:
grunt.initConfig({
/* ... */
variablize: {
target: {
input: "resources/Config.json",
output: "dist/Config.js",
variable: "Config",
property: grunt.option('profile')
}
}
});
run grunt --profile=test, and use it this way:
$(document).ready(function(){
var myURL = Config.apiUrl;
});

This might not be the best way to do it. But this works. :)
You can set value to a javascript variable with your server side code itself.
$(document).ready(function(){
var myURL = "<%=urlFromServer%>"; //Assuming you are using JSP <%= ..%>
});
This global variable holds the value. You can re use this variable wherever you need it.

Related

How to change the URL prefix for fetch calls depending on dev vs prod environment?

This is a follow-up to my previous question.
I'm using Vite, and the dev server comes with a proxy server, such that I can specify a proxy like this:
proxy: {
'/v1': {
target: 'https://127.0.0.1:8080',
changeOrigin: true
}
}
And then a fetch call called like this:
fetch("/v1/get-users");
Will effectively be transformed into this:
fetch("https://127.0.0.1:8080/v1/get-users");
This works perfectly, because that is where my API server is hosted on my dev environment.
However, this will fail on prod, because there my API server is instead hosted at https://api.mysite.com
Ideally I could just somehow express that my prod prefix should instead be https://api.mysite.com instead of https://127.0.0.1:8080, but apparently Vite does not use a proxy server with its build output.
How do people normally handle this? The only thing I could think of is perhaps something like this, but it seems a bit gross:
function getPrefix() {
return location.hostname === "mysite.com" ? "https://api.mysite.com" : "";
}
fetch(getPrefix() + "/v1/get-users");
But it just seems kind of messy, and cumbersome to have to add that to every fetch call.
I could make a fetch wrapper as well, but I'm just wondering if this sort of thing is how people solve this to begin with, or if there is a more "best-practice" solution for this common problem.
The simplest (and cleanest) solution I can think of is to replace the values in your code. This is often done for things like process.env.NODE_ENV or the like, but this is certainly a standard usecase.
You can use rollup-replace-plugin vite-plugin-replace, which allows you to replace values in your code.
Change vite.config.js to:
// ...
import { replaceCodePlugin } from "vite-plugin-replace";
export default {
// ...
plugins: [
replaceCodePlugin({
replacements: [
{
from: "__SITE_BASE__",
to: process.env.NODE_ENV === "production"
? "https://api.mysite.com" // production site
: "" // development proxy,
}
],
}),
],
});
In your code, you would do:
fetch("__SITE_BASE__/v1/get-users");
This would be advantageous over your current approach, as firstly, you can easily change your site base (instead of digging in your code to find that function), secondly, it's easier to write, and lastly you have static data, so you don't have to call the function every time the user loads the page, which is better for performance anyways.

node.js: How to include external .js from another server?

I am trying to include .js files that are located on another server into my node.js program. Is this possible?
I can´t use require('./local_file.js') and get something over the Internet like this: require('http://www.server.com/path/file.js');
I´ve tried to make a http.request and then use eval(), but the scope is all wrong and the functions inside the external file remain undefined:
var source_string = load_contents_via_http('http://www.server.com/folder/file.js');
var source_string += '\n run_my_function();';
eval(source_string);
Does anyone have any suggestions?
Edit (Solution):
I solved my problem by repacking the essential parts into the script that runs on my local server, as mentioned by #zackehh. Then I used http.request to load and eval specific parts from the remote server when needed. Since the most important code was running on the server locally, the imported extra code was easier to add.
Here is a example on how I solved the problem:
var EssentialObject = {};
EssentialObject.ServerFunctions = {};
EssentialObject.ServerFunctions.Init = function (){
var external_code = load_contents_via_http('http://www.server.com/file.js');
var eval_this = external_code.substr(
external_code.indexOf('EssentialObject.Addons = {}'),
external_code.indexOf('// End of EssentialObject.Addons')-external_code.indexOf('EssentialObject.Addons = {}')
);
eval ( eval_this );
eval ( "EssentialObject.Addons.test=true; console.log('Eval Done')" ); // Check if it works
};
Disclaimer: this technically isn't an answer to the question, I feel it should be classed as one, as it moves towards a solution to the problem.
You could do it with a simple http request, but I don't recommend it. You would be better off repacking the things you need on both servers in npm modules and requiring them in. This allows you share your code pretty painlessly. If you don't want your code public, npm also has new private module options.

Hardcoded Web API calls in code versus multiple environments

Actually, I hardcode my Web API calls like this :
$http.get('my/production/web/api/method');
If I want to switch to a test environment, which is another Web API, I don't want to manually change every hardcoded call in the code.
Is there a way to solve this by using a configuration file or has angularjs a feature to handle that ?
Like domakas said, use your own config file:
var config={
environment:'production'
//environment:'dev'
};
$http.get('my/'+config.environment+'/web/api/method');
Make your own function:
function queryWebApi(method) {
$http.get('my/'+config.environment+'/web/api/'+method);
}

How to inject an entire module dynamically in AngularJS?

Alright, so I'm trying to set-up $httpBackend to use as a mock server for local development against an API. I have two services in separate modules: search and searchMock:
search returns a $resource object that exposes the API verbs when in staging or production environments, and works as expected
searchMock exposes $httpBackend which, in turn, responds with mock JSON objects, and works as expected by itself
I have another service, APIInjector, that determines what the current environment is based on a config file that's included dynamically by Grunt when the app is built and injects either search or searchMock accordingly.
My problem is, as far as I can tell from searching high and low, $httpBackend needs to be set-up within a module's run method. The problem with this is I can't inject the run method within my APIInjector's conditional logic.
How can I expose $httpBackend if the dev environment condition is met, and my $resource service otherwise? Note that I didn't include the calling controller's or the searchService's code, but I can if needed for clarification.
searchMock:
var searchMockService = angular.module('searchMockService', []);
searchMockService.run([
'$httpBackend',
function($httpBackend) {
results = [{name: 'John'}, {name: 'Jane'}];
$httpBackend.whenGET('/search').respond(results);
}]);
APIInjector:
var APIInjectorService = angular.module('APIInjectorService', [
'searchService',
'searchMockService'
]);
APIInjectorService.factory('APIInjector', [
'$injector',
'ENV_CONF',
function($injector, ENV_CONF) {
var isDevelopment = ENV_CONF.IS_DEVELOPMENT;
// Check to see if we're in dev and, if so, inject the local API services
if (isDevelopment) {
return {
Search: // Not sure what to do here to expose searchMock's run method???
};
} else {
return {
Search: $injector.get('Search') // This returns $resource from the search service, as expected
};
}
}]);
Just to put a final point on this question, I ultimately pulled this out to my build script (Grunt) and abandoned the above approach completely. Instead, I'm passing in the environment I want to build for (development, staging, production, etc) as a flag to Grunt and then loading the appropriate API service during the build.
Hope that helps someone else trying to implement backendless development!
As bad as it looks at first place, i'd use the document.write method,if you are not using requirejs(it shouldnt be a problem with requirejs).
1 - bootstrap your ENV_CONF variable in a script tag as first script in HEAD tag.
2 - load all the common scripts
3 - test the ENV_CONF.IS_DEVELOPMENT in another SCRIPT tag.
4 - if true document.write('<script src="angular-mocks.js"></script><script src="main-dev-module.js"></script>')
5 - if false document.write('<script src="main-prod-module.js"></script>')
6 - profit
That way you dont need to deal with ENV_CONF.IS_DEVELOPMENT inside your modules.
html5 boilerplate uses this technique to either fetch jquery from a cdn or locally
https://github.com/h5bp/html5-boilerplate/blob/master/index.html
Edit: I'm sure there are cleaner ways to inject services dynamically in angularjs,but that's what i'd do.I'd like someone to propose a better alternative.

Javascript Build Tools To Toggle Urls/Variables Between Production/Deployment

I am beginning my first big javascript project! I had a question about deployment. I am using ajax calls to a webservice. To set this up I have a static JS file with code like:
var API_URL_ROOT = 'http://api.example.com/';
var IN_DEVELOPMENT = True;
if (IN_DEVELOPMENT) {
API_URL_ROOT = 'http://localhost.com/api';
}
$.get(API_URL_ROOT)
I am using python/fabric to deploy. I was wondering if there were any prebuilt tools for handling the static analysis/manipulation of the javascript files., Right now it leaves toggling up to the commiters
I was planning on a deployment process like:
issue deploy command
"build" JS, by setting all values to production values (ie. IN_DEVELOPMENT = False)
Minify JS
Deploy code to production servers
I was thinking of just using sed or something to do the IN_DEVELPMENT = False replacement. I have looked at some of the popular minification tools and none seem to offer this sort of functionality.
I would assume that this is a pretty common problem for applications. How is it usually handled? Any help would be appreciated. Thank you
I recently read an article on hackernews from mozilla:
In the Mozilla Persona code base, we frequently expose difficult to
test private functions to the public interface, clearly marking the
extra functions as part of a test API. While other developers are
still able to call these private functions, the author’s intentions
are clear.
...
publicFunction: function() {
return "publicFunction can be invoked externally but "
+ privateFunction();
}
// BEGIN TESTING API
,
privateFunction: privateFunction
// END TESTING API
};
// privateFunction is now accessible via the TESTING API
function privateFunction() {
...
Code between the // BEGIN TESTING API and //END TESTING API
pseudo-markers can be removed for production during the build process.
So other companies are definitely doing this. Are there premade tools to facilitate the JS build proccess that can remove this code? I glanced at a number of their projects on github and didn't see any. Thank you
We are using dojo
And in dojo you can use conditional exclusions for the build version of your js in order to exclude parts of your code that you would not want in your build js. Hope this helps.
eg:
var API_URL_ROOT = 'http://api.example.com/';
//>>excludeStart("dev",!pragmas.dev);
var IN_DEVELOPMENT = True;
//>>excludeEnd("dev");
if (IN_DEVELOPMENT) {
API_URL_ROOT = 'http://localhost.com/api';
}
$.get(API_URL_ROOT)

Categories

Resources