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:

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: emberapp.development-deploy.tld emberapp.staging-deploy.tld

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:

var ENV = {

	APP: {
		usingCors: false,
		corsWithCreds: false,
		apiURL: null

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

switch (environment) {
	case 'local-backend':
		ENV.APP.usingCors = true;
		ENV.APP.corsWithCreds = true;
		ENV.APP.apiURL = ''
	case 'development-backend':
		ENV.APP.usingCors = true;
		ENV.APP.corsWithCreds = true;
		ENV.APP.apiURL = 'https://development-backend.tld/'
	case 'staging-backend':
		ENV.APP.usingCors = true:
		ENV.APP.corsWithCreds = true;
		ENV.APP.apiURL = 'https://staging-backend.tld/'

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

ember s --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):

import JSONAPIAdapter from 'ember-data/adapters/json-api';
import ENV from '../config/environment';

export default JSONAPIAdapter.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 };

	return this._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 ember s 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):

/*global jQuery */
import ENV from '../config/environment';

export function initialize() {
	if (ENV.APP.usingCors) {
		(function($) {
			var _old = $.ajax;
			$.ajax = function() {
				var url, 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]);

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.