Skip to content

Instantly share code, notes, and snippets.

@superKalo
Last active September 18, 2017 18:05
Show Gist options
  • Save superKalo/36bfb70435b0e788f0c780267a1df660 to your computer and use it in GitHub Desktop.
Save superKalo/36bfb70435b0e788f0c780267a1df660 to your computer and use it in GitHub Desktop.
Useful Abstractions When Working With Remote Data in JavaScript

Define an API Abstraction

Define an API module, that does the actual AJAX requests:

const API = {
    /** Simple service for generating different HTTP codes. */
    url: 'http://httpstat.us/',

    /**
     * fetch() will only reject a promise if the user is offline,
     * or some unlikely networking error occurs, such a DNS lookup failure.
     * However, there is a simple `ok` flag that indicates
     * whether an HTTP response’s status code is in the successful range or not.
     */
    _handleError(_response) {
        return _response.ok ? _response : Promise.reject(_response.statusText);
    },

    get(_endpoint) {
        return window.fetch(this.url + _endpoint, {
            method: 'GET',
            headers: new Headers({
                'Accept': 'application/json'
            })
        })
        .then(this._handleError)
        .catch( error => { throw new Error(error) })
    },

    post(_endpoint, _body) {
        return window.fetch(this.url + _endpoint, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: _body
        })
        .then(this._handleError)
        .catch( error => { throw new Error(error) })
    }
};

Why this is good:

  • Fetch API is not tightly coupled with our code. We can alter the implementation and implement jQuery $.ajax() or zlFetch instead only by tweaking the API module. As long as we return a Promise - the implementation in all other modules stays the same.

Manipulate the Data in a Repository

Define Object that handles the data (Repository-ish pattern):

import API from './api.js'; // Import it into your code however you like

const WeatherRepository() {
    _normalizeData(currentWeather) {
        // Take only what our app needs and nothing more.
        const { t, w, p } = currentWeather;

        return {
            temperature: t,
            windspeed: w,
            pressure: p
        };
    },

    /**
     * Get current weather.
     * @return {Promise}
     */
    get(){
        return API.get('/weather')
            .then(this._normalizeData);
    }
}

Why this is good:

  • Throughout our codebase via WeatherRepository.get() we access meaningful and semantic attributes like .temperature and .windspeed instead of t and s.
  • Via the _normalizeData() we expose only parameters we need and we simply don't include (hide) all others.
  • If the response attributes names change (or we need to wire-up another API with different response structure), we only need to tweak _normalizeData().
  • If we want to implement a caching mechanism, as long as WeatherRepository.get() returns a Promise, we don’t need to change the implementation in any other module.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment