Skip to content

Instantly share code, notes, and snippets.

@tomdale
Last active October 16, 2019 09:01
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomdale/203ea125acd0be8b5f1f to your computer and use it in GitHub Desktop.
Save tomdale/203ea125acd0be8b5f1f to your computer and use it in GitHub Desktop.
Ember Data / Ember Ajax / Ember Fetch proposal

Networking in Ember

Motivation

Most client-side applications need to fetch data over the network. In the Ember.js ecosystem, many developers use a high-level abstraction, like Ember Data, that standardizes network access via an adapter pattern.

For those who want more low-level control over data fetching, it is common to use techniques that are generally well-known in the wider web development community. For example, because jQuery returns promises and Ember's router understands promises, it's common for developers to use $.ajax() and its derivatives to fetch data:

model() {
  return $.getJSON('/users.json');
}

(To improve integration with Ember's run loop and to fix deficiencies in jQuery's promise implementation, many Ember developers use ic-ajax instead: a small, API-comaptible shim around jQuery.)

This generally works well but suffers from two major problems motivating this proposal:

  1. jQuery, ic-ajax, etc. don't work in non-browser environments, such as FastBoot apps running in Node.js.
  2. Sharing configuration between Ember Data and low-level network requests is difficult.

Many developers take a "mix and match approach," using Ember Data in most of their app but still dropping down to raw AJAX in cases where they want precise, programmatic control.

For these apps, it's important that they work correctly in FastBoot (where, presently, only Ember Data works). Additionally, often both the Ember Data adapter and low-level network requests share configuration concerns, like adding authentication headers to outbound requests.

Description

I propose a three-layered approach for maximizing compatibility, reusability and developer convenience:

  1. Ember Data, an ORM where network access is managed via the adapter.
  2. Ember Ajax, a full-featured library for raw network access. This library provides idiomatic Ember APIs and additional ergonomics on top of the low-level APIs provided by the browser.
  3. Ember Fetch, a very low-level shim over the window.fetch() API, polyfilled in environments where it's not available.

This document discusses each layer in turn, from the bottom up.

Low-Level: Ember Fetch

The Fetch API provides an interface for fetching resources (e.g., across the network.) It will seem familiar to anyone who has used XMLHttpRequest, but the new API provides a more powerful and flexible feature set.

One of the explicit goals of Ember.js is to provide developers with the ability to use future features of the web platform today. For example, Ember developers have been using ES6 modules for more than a year, adopting them far earlier than most other web developers.

We create a community of early adopters by focusing on making adoption as easy as possible, and encourage migrating the entire community to the new way of doing things. This creates a "network effect" for adoption of the new feature.

We have the same opportunity to standardize on the future today with the fetch() API, a modern revamp of the stalwart XMLHttpRequest.

Ember Fetch is designed to be as small, targeted and low-overhead as possible. It simply polyfills the fetch() API, including inside Node.js (for FastBoot) and in browsers where it is not yet implemented.

This small, focused nature means that anyone writing a library that both requires network access and will be used by Ember apps can support FastBoot without worrying about bloating their dependencies.

Additionally, it gives us an easy way to tell people to begin refactoring their code away from $.ajax() and towards the more modern fetch(). Once the implementation of fetch() is widespread, we can drop the XMLHttpRequest-based polyfill and the implementation of Ember Fetch becomes tiny indeed.

Mid-Level: Ember Ajax

Ember Ajax (already in existence) will be refactored to depend on Ember Fetch and direct all network access through it. Instead, it will focus on providing Ember-idiomatic APIs on top of the primitive fetch() to make working with the network easier for Ember developers.

Specifically, that includes:

  1. Shorthand methods (get(), put(), etc.)
  2. Custom headers attached to every request (AJAX preflight filters)
  3. Custom host configuration
  4. Error types (InvalidError, ForbiddenError, etc) rather than status codes
  5. Customizing response categorization (success or error)

For more, see ember-ajax.

The expectation would be that most Ember apps that previously used $.ajax() would instead use the Ember Ajax service.

High-Level: Ember Data

Ember Data's built-in adapters would require and use Ember Ajax. This gives adapter authors the full suite of ergonomic improvements on top of fetch() to use in their adapters.

Additionally, it means that any configuration developers made to Ember Ajax (how to categorize responses, custom auth headers, etc.) would all be inherited by Ember Data.

This change would make Ember Data work with FastBoot out of the box and we could eliminate the monkeypatch that is required currently.

Unresolved Issues

Shared Configuration

While Ember Data sharing configuration with the ajax service is convenient and correct in many scenarios, there may be scenarios where it is incorrect.

For example, imagine you are using Ember Data to talk to a new API on api.example.com. Your app still has some legacy UI that relies on an older API which lives at api-legacy.example.com. It would be problematic if you could not set one host for your Ember Data adapter and another as the default for the ajax service.

One solution may be to allow the user to provide an override on the adapter. For example, the Ember Data adapter would not specify a host by default, falling back to the ajax service's configuration. However, if you set a host on the adapter, it would always specify that host explicitly when it requested data.

@kellyselden
Copy link

I made https://github.com/kellyselden/ember-data-fetch-support that might help official ember-data fetch support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment