util_Fetcher.js

import { Helper } from "../util/Helper.js";

/**
 * Fetcher
 * Wrapper of the native Fetch API
 *
 * @example <caption>Using callbacks</caption>
 * const fetcher = new Fetcher( 'https://api.example.com' );
 * fetcher.get( '/books', function( err, result ) { } );
 *
 * @example <caption>Using await</caption>
 * const fetcher = new Fetcher( 'https://api.example.com' );
 * try
 * {
 *     const result = await fetcher.get( '/books' );
 * }
 * catch( e )
 * {
 *     // Handle error
 * }
 *
 */
class Fetcher
{
    /**
     * Construcotr
     * @param {string} url - Base url
     * @param {Headers=} headers - Header data.
     */
    constructor( url = '', headers = {} )
    {
        this.url = Helper.trim( url, '/' );
        if ( this.url.length <= 1 )
        {
            throw new Error( 'No endpoint set.' );
        }

        this.headers = new Headers( headers );
    }

    /**
     * GET call
     * @param {string} endpoint - API endpoint
     * @param {function=} [cb=null] - Callback function
     * @returns {Promise<any|undefined>}
     */
    async get( endpoint, cb = null )
    {
        let r = Helper.trim( endpoint, "/" ),
            req = new Request( this.url + '/' + r, this._createFetchOptions( "GET" ) );

        return await this._fetch( req, cb );
    }

    /**
     * POST call
     * @param {string} endpoint - API endpoint
     * @param {object=} [data={}] - Post data
     * @param {function=} [cb=null] - Callback function
     * @returns {Promise<any|undefined>}
     */
    async post( endpoint, data = {}, cb = null )
    {
        let r = Helper.trim( endpoint, "/" ),
            req = new Request( this.url + '/' + r, this._createFetchOptions( "POST", data ) );
        return await this._fetch( req, cb );
    }

    /**
     * DELETE call
     * @param {string} endpoint - API endpoint
     * @param {function=} [cb=null] - Callback function
     * @returns {Promise<any|undefined>}
     */
    async delete( endpoint, cb = null )
    {
        let r = Helper.trim( endpoint, "/" ),
            req = new Request( this.url + '/' + r,  this._createFetchOptions( "DELETE" ) );
        return await this._fetch( req, cb );
    }

    /**
     * PUT call
     * @param {string} endpoint - API endpoint
     * @param {object=} [data={}] - PUT data
     * @param {function=} [cb=null] - Callback function
     * @returns {Promise<any|undefined>}
     */
    async put( endpoint, data = {}, cb = null )
    {
        let r = Helper.trim( endpoint, "/" ),
            req = new Request( this.url + '/' + r, this._createFetchOptions( "PUT", data )  );
        return await this._fetch( req, cb );
    }

    /**
     * PATCH call
     * @param {string} endpoint - API endpoint
     * @param {object=} [data={}] - Patch data
     * @param {function=} [cb=null] - Callback function
     * @returns {Promise<any|undefined>}
     */
    async patch( endpoint, data = {}, cb = null )
    {
        let r = Helper.trim( endpoint, "/" ),
            req = new Request( this.url + '/' + r, this._createFetchOptions( "PATCH", data ) );
        return await this._fetch( req, cb );
    }

    async _fetch( req, cb = null )
    {
        if ( cb )
        {
            fetch( req )
                .then( response => response.json() )
                .then( json => cb( null, json ) )
                .catch( error => cb( error, null ) );
        }
        else
        {
            try
            {
                const response = await fetch( req );
                let json = null;
                try {
                    json = await response.json();
                }
                catch ( jsonErr )
                {
                    json = null;
                }
                return {
                    status : response.status,
                    json
                };
            }
            catch( err )
            {
                console.error( err );
                return null;
            }
        }
    }

    _createFetchOptions( method, data = null )
    {
        const opts = {
            "method" : method.toUpperCase(),
            "headers" : this.headers
        };
        if ( Helper.isPlainObject( data ) )
        {
            opts.body = JSON.stringify( data );
        }
        return opts;
    }
}

export { Fetcher };