Marc
Marc

Reputation: 3892

How to support multiple API hosts with Ember data?

I have an Ember application (with ember data) that is run on a user's machine. There's an API server running on their machine, and one running online. I need to allow users to choose where a model is persisted (the API running on their machine, or the online API). So, two API hosts:

http://localhost:3000
and
http://api.example.com

When a user creates a record, they can set wether they want the record saved locally (through the local API server), or saved online. I persist this choice to a value on the record called dataSource.

So, depending on the record dataSource, I need to set the ember RestAdapter host for the model to the correct value. I understand one can override adapters on a per model basis. For example, I could create a RecordAdapter and manually set the host to a value. However, the host depends on a value in the record, and I'm not sure how to accomplish this with Ember Data, since the rest adapter "host" is a property, not a function.

http://emberjs.com/api/data/classes/DS.RESTAdapter.html#property_host

User flow example:

Upvotes: 0

Views: 1051

Answers (1)

Andrew Hacking
Andrew Hacking

Reputation: 6366

You need to override buildURL on your adapter:

As per Ember Data source, the default implementation for the RESTAdapter is:

/**
    Builds a URL for a given type and optional ID.

    By default, it pluralizes the type's name (for example, 'post'
    becomes 'posts' and 'person' becomes 'people'). To override the
    pluralization see [pathForType](#method_pathForType).

    If an ID is specified, it adds the ID to the path generated
    for the type, separated by a `/`.

    @method buildURL
    @param {String} type
    @param {String} id
    @param {DS.Model} record
    @return {String} url
  */
  buildURL: function(type, id, record) {
    var url = [],
        host = get(this, 'host'),
        prefix = this.urlPrefix();

    if (type) { url.push(this.pathForType(type)); }

    //We might get passed in an array of ids from findMany
    //in which case we don't want to modify the url, as the
    //ids will be passed in through a query param
    if (id && !Ember.isArray(id)) { url.push(encodeURIComponent(id)); }

    if (prefix) { url.unshift(prefix); }

    url = url.join('/');
    if (!host && url) { url = '/' + url; }

    return url;
  },

As you can see, buildURL on your adapter is provided the record so the URL can configured based on that.

The following example shows how you can choose the prefix based on the isLocal property on your model instances:

// app/adapters/application.js

import Ember from 'ember';
import DS from 'ember-data';
var get = Ember.get;

export default DS.RESTAdapter.extend({

  buildURL: function(type, id, record) {
    var url = [],
        host = get(this, 'host'),
        prefix;

    // choose prefix based on model setting
    if (record && get(record, 'isLocal')) {
      prefix = 'http://localhost:3000';
    } else {
      prefix = this.urlPrefix();
    }

    if (type) { url.push(this.pathForType(type)); }

    //We might get passed in an array of ids from findMany
    //in which case we don't want to modify the url, as the
    //ids will be passed in through a query param
    if (id && !Ember.isArray(id)) { url.push(encodeURIComponent(id)); }

    if (prefix) { url.unshift(prefix); }

    url = url.join('/');
    if (!host && url) { url = '/' + url; }

    return url;
  },
});

Upvotes: 5

Related Questions