api-fetcher-treeline

Fetch wrapper for requests to a REST API backend

Usage no npm install needed!

<script type="module">
  import apiFetcherTreeline from 'https://cdn.skypack.dev/api-fetcher-treeline';
</script>

README

api-fetcher-treeline

Greenkeeper badge

Install

npm install --save api-fetcher-treeline

To polyfill Promise and fetch see here

Basic usage

// In api.js ...
import { ApiFetcher } from 'api-fetcher-treeline/dist';

const setupApiFetcher = urlPrefix => {
    const api = new ApiFetcher(urlPrefix);

    if (process.env.NODE_ENV === 'production') {
        api.setFetchOptions(/*Custom fetch options*/);
    }

    return api;
};

const api = setupApiFetcher('https://somewhere.com/api');

export default api;

Then use it in react components:

import React from 'react';
import api from '../api';

export default class MyComponent extends React.Component {
    state = { todos: [] };

    render() {
        return <div id="some-content" />;
    }

    // Send a GET request to endpoint /todos wrapped
    // in the ability to cancel it if the component unmounts
    // before the fetch has completed
    componentDidMount() {
        this.fetchRequest = api.cancelableGet('/todos');
        const { promise } = this.fetchRequest;
        promise.then(todos => /*Do something with todos*/)
    }

    // Always cancel your requests in componentWillUnmount. This will have no effect
    // if the Promise has already resolved and its side effects have been issued.
    componentWillUnmount() {
        this.fetchRequest.cancel();
        // or `api.revoke(this.fetchRequest);`
    }
}

Here is the class definition with some comments.

import ApiSource from './ApiSource';
import { makeCancelable } from './makeCancelable';

export class ApiFetcher {
    constructor(urlPrefix, config) {
        this.api = new ApiSource(urlPrefix, config);
    }

    // Use this to alter the default options passed to the fetch api
    setFetchOptions(fetchOptions) {
        this.api.setFetchOptions(fetchOptions);
    }

    // Sends a plain GET request using the Fetch api
    get(endpoint) {
        return this.api.call('GET', endpoint);
    }

    // Sends a 'WrappedPromise' GET request (see notes below...)
    cancelableGet(endpoint) {
        return makeCancelable(this.get(endpoint));
    }

    post(endpoint, body) {
        return this.api.call('POST', endpoint, body);
    }

    // 'body' is the request body passed to fetch. It will be Json.stringify()ed
    cancelablePost(endpoint, body) {
        return makeCancelable(this.post(endpoint, body));
    }

    del(endpoint) {
        return this.api.call('DELETE', endpoint);
    }

    cancelableDel(endpoint) {
        return makeCancelable(this.del(endpoint));
    }

    call(httpMethod, endpoint, body) {
        return this.api.call(httpMethod, endpoint, body);
    }

    cancelableCall(httpMethod, endpoint, body) {
        return makeCancelable(this.call(httpMethod, endpoint, body));
    }

    revoke(cancelableRequest) {...} // cancels a cancelableRequest
}

All the "cancelable" methods return a "WrappedPromise" object that is shaped like this:

const wrappedPromiseExample = {
    promise: [Promise], // This key points to the actual pending Promise
    cancel: [Function], // This function will reject() the Promise
};

Here are the default options passed on to the browser fetch api

const defaultFetchOptions = {
    headers: {
        'Access-Control-Allow-Origin': '*',
        'Content-Type': 'application/json ; charset=utf-8',
    },
    credentials: 'include',
    mode: 'cors',
};

To overwrite these options, use the setFetchOptions() method

const setupApiFetcher = urlPrefix => {
    const api = new ApiFetcher(urlPrefix);

    if (process.env.NODE_ENV === 'production') {
        api.setFetchOptions({ mode: 'cors', ...myOtherProdFetchOptions });
    }

    return api;
};

If for some reason you neeed to hook into the response object, use config.onResponse()

const config = {
    onResponse: response => {
        if (response.status !== 401) {
            return response.json();
        } else {
            throw new Error('Response is not ok');
        }
    },
};

const api = new ApiFetcher('http://hello.com/api', config);
api.get("/todos").then(todos => /* Do something w/ your todos here */);

Contributing

To run the test suite, run npm test.

License

MIT.