Using CORS and Ember-Data to point an Ember app to multiple backend deployments

At Curious Minds we have developers working from different offices doing backend and frontend work on the same projects, so we need a deployment scenario that allows developers to make local changes without fear of breaking things for everyone, but also to be able to develop against a shared environment so that bugs can be replicated and everyone can be sure they’re on the same page. For the backend, this is simple enough: we deploy to three different backend servers besides the production system:

a ‘local’ deployment: a Vagrant virtual machine for fullstack and backend developers to run on their local machines while working on separate branches before they’ve been merged in to the main development branch.

A ‘development’ deployment, on a public-facing server (or at least accessible by our developers anywhere in the world) that replicates the production environment closely. Here code is bleeding edge and it’s ok if things break.

A ‘staging’ deployment, identical to the development deployment. Here we merge in changes that appear stable and tested on the ‘development’ server for a more thorough review before deployment to production. Here we treat the data as it if were production data and have testing done by real-world users.

For the frontend, we’ve been using Ember.js, and here things get a little more complicated. Developers need to be able to point their local
ember serve instance at all three (local, development, staging) backend deployments so that they can ensure their local development branches of the frontend will work against the different deployments. This kind of flexibility is especially helpful because the frontend and backend are not always going to be synced up in terms of stability: sometimes a backend API feature might be considered stable, but the frontend feature that makes use of that API is still in progress. (This only applies to active development of the ember app. For deployments we use ember-cli-deploy). In that case we’ll want to point a “development” branch of the frontend against the staging backend. Or, if a frontend bug is discovered in staging — or even production! — that requires a hotfix, it’s helpful to be able to make the fix using
ember serve against the backend it will be deployed for.

CORS Headers

So how do you point a local emberapp against multiple backends? Our solution was to configure Cross Origin Request Headers headers on our backend deployments, and configure Ember Data’s adapter to make cross-domain requests. To do this, you’ll have to pick a specific origin URL that you’ll use to hit your ember server for each backend, and configure the
Origin on each backend to use this URL:

1

2

3

Access-Control-Allow-Origin:http://emberapp.app-deploy.tld:4200/

Access-Control-Allow-Credentials:true

Access-Control-Allow-Methods:GET,POST,PATCH,DELETE,OPTIONS

Note the URL used in the
Allow-Origin header. It has to be the exact address used to hit the ember server, including protocol (
http) and port (
4200). You’ll also have to also map it to
localhost in your local DNS configuration (for example, in your
/etc/hosts file or your dnsmasq settings). So for a three tiered development deployment like we use, you’d configure your
/etc/hosts file like so:

Be sure to also configure the
Access-Control-Allow-Headers with any other headers necessary (wildcards (
*) are not allowed, if you need the equivalent to a wildcard, you can use this list of headers.)

Configure Ember and Ember-data to support CORS requests

Environment configuration

Once you have the CORS headers configured, you’re ready to set up your Ember app to support cross domain requests to your api. The easiest way is to configure ember environments, one for each backend, in
config/environment.js. First we add some defaults to the
APP key of the environment config:

config/environment.js

JavaScript

1

2

3

4

5

6

7

8

9

10

varENV={

...

APP:{

...

usingCors:false,

corsWithCreds:false,

apiURL:null

}

};

Next, we use a
switch on
environment to configure our API URLs:

config/environment.js

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

switch(environment){

case'local-backend':

ENV.APP.usingCors=true;

ENV.APP.corsWithCreds=true;

ENV.APP.apiURL='http://local-backend.app:8000'

break;

case'development-backend':

ENV.APP.usingCors=true;

ENV.APP.corsWithCreds=true;

ENV.APP.apiURL='https://development-backend.tld/'

break;

case'staging-backend':

ENV.APP.usingCors=true:

ENV.APP.corsWithCreds=true;

ENV.APP.apiURL='https://staging-backend.tld/'

break;

}

This will enable to point the emberapp to the backend we want to test against by just setting the environment wehn running
ember serve, like so

1

embers--environment local-backend

(Substitute
local-backend for
development-backend or
staging-backend to point Ember at the other backends). Then point your browser at the ember server via the URL you’ve configured as the CORS
Origin, e.g., http://emberapp.development-backend.tld:4200/ for the development environment.

Patching the Ember-Data Adapter

Once you’ve got the environments set up, you’re ready to patch ember-data’s “adapter” to support CORS requests against the environment specific API URL by setting up your
app/adapters/application.js like so (here we use the JSONAPIAdapter but this override will work with any adapter provided by Ember Data):

app/adapters/application.js

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

import JSONAPIAdapter from'ember-data/adapters/json-api';

import ENV from'../config/environment';

export defaultJSONAPIAdapter.extend({

namespace:'your-api-namespace',

host:ENV.APP.apiURL,

headers:{

'X-Requested-With':'XMLHttpRequest'

},

ajax(url,method,hash){

if(ENV.APP.usingCors){

if(hash===undefined){hash={};}

hash.crossDomain=true;

if(ENV.APP.corsWithCreds){

hash.xhrFields={withCredentials:true};

}

}

}

returnthis._super(url,method,hash);

});

This override is pretty simple. It sets the host to the configured API URL, and ensures that XHR requests made by the adapter have the
crossDomain and
withCredentials field set correctly. The browser will take care of the rest.

Patching jQuery’s ajax function

For many applications, this will be all you need to do. Running
embers against the configured environments will direct your ember data requests to the respective backend seamlessly. But you’ll still run into problems if you bypass Ember Data’s adapters at any point and make direct ajax calls through jQuery.

To handle that sort of situation, you’ll have to patch jQuery’s
ajax function to also support CORS requests. Doing so is not so hard, and jQuery is stable enough that it likely won’t break in the near future. Here’s how we get it done (put this in
app/initializers/services.js or some other initializer that will run early):

app/initializers/services.js

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

/*global jQuery */

import ENV from'../config/environment';

export functioninitialize(){

if(ENV.APP.usingCors){

(function($){

var_old=$.ajax;

$.ajax=function(){

varurl,settings,apiURL=ENV.APP.apiURL;

/* Handle the different function signatures available for $.ajax() */

if(arguments.length===2){

url=arguments[0];

settings=arguments[1];

}else{

settings=arguments[0];

}

settings.crossDomain=true;

if(!settings.xhrFields){

settings.xhrFields={};

}

settings.xhrFields.withCredentials=true;

if(!url){

url=settings.url;

}

/* If we still don't have an URL, execute the request and let jQuery handle it */

if(!url){

return_old.apply(this,[settings]);

}

/* combine the apiURL and the url request if necessary */

if(!url.includes(apiURL)){

/* Do we need a '/'? */

if(url[0]!=='/'&&apiURL[apiURL.length-1]!=='/'){

url='/'+url;

}

url=apiURL+url;

}

settings.url=url;

return_old.apply(this,[settings]);

};

})(jQuery);

}

}

This override is again fairly straightforward. It should handle 9 out of 10 cases of ajax requests in your emberapp.

About Curious Minds
We are a web development firm in New York and Chicago, providing development resources and consulting for websites and mobile apps since 2004.

“The work that Curious Minds does for us is excellent and professional.”

“They are problem solvers and creative thinkers and we are so pleased to be working with them.”

– Lynn Herring, Executive Creative Director, CharlieDog Advertising

“Curious Minds takes the time to understand my business and offers technological advice that is sound, practical, and economical.”

– David Polakoff, Chief Operating Officer, Global Auction Network

“Their skill is unsurpassed, saving you thousands of hours of wasted time and thousands of dollars of wasted money.”

“Curious Minds is a refreshing departure from most web development firms that you hire to develop a project.”

– Chris Kay, CEO, Blue Global Media, Inc.

“They have delivered exemplary customer service, and have shown outstanding attention to detail.”

“Curious Minds has taken the time to understand our needs and business drivers and truly collaborated with us to produce a product that is both technically solid and visually impressive.”