npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nativescript-apiclient

v3.0.0

Published

NativeScript module for simply calling HTTP based APIs.

Downloads

45

Readme

npm npm

NativeScript API Client

A NativeScript module for simply calling HTTP based APIs.

Donate

NativeScript Toolbox

This module is part of nativescript-toolbox.

License

MIT license

Platforms

  • Android
  • iOS

Installation

Run

tns plugin add nativescript-apiclient

inside your app project to install the module.

Demo

For quick start have a look at the plugin/index.ts or use the "IntelliSense" of your IDE to learn how it works.

Otherwise...

Usage

Import

import ApiClient = require("nativescript-apiclient");

Example

import ApiClient = require("nativescript-apiclient");
import HTTP = require("http");

interface IUser {
    displayName?: string;
    id?: number;
    name?: string;
}

var client = ApiClient.newClient({
    baseUrl: "https://api.example.com/users",
    route: "{id}",  
});

client.beforeSend(function(opts: HTTP.HttpRequestOptions, tag: any) {
                      console.log("Loading user: " + tag);
                      
                      // prepare the request here
                  })
      .clientError(function(result: ApiClient.IApiClientResult) {
                       // handle all responses with status code 400 to 499
                   })
      .serverError(function(result: ApiClient.IApiClientResult) {
                       // handle all responses with status code 500 to 599
                   })
      .success(function(result: ApiClient.IApiClientResult) {
                    // handle all responses with that were NOT
                    // handled by 'clientError()' and 'serverError()'
                    // 
                    // especially with status code less than 400 and greater than 599
                    
                    var user = result.getJSON<IUser>();
               })
      .error(function(err: ApiClient.IApiClientError) {
                 // handle API client errors
             })
      .completed(function(ctx: ApiClient.IApiClientCompleteContext) {
                     // invoked after "result" and "error" actions
                 });

var credentials = new ApiClient.BasicAuth("Marcel", "p@ssword!");

for (var userId = 1; userId <= 100; userId++) {
    // start a GET request
    // 
    // [GET]  https://api.example.com/users/{id}?ver=1.6.6.6
    client.get({
        authorizer: credentials,
    
        // request headers
        headers: {
            'X-MyHeader-TM': '5979',
            'X-MyHeader-MK': '23979'
        },
        
        // URL parameters
        params: {
            ver: '1.6.6.6'
        },
    
        // route parameters
        routeParams: {
            id: userId.toString()  // {id}
        },
        
        // global value for all callbacks 
        tag: userId
    });
}

Routes

Routes are suffixes for a base URL.

You can define one or parameters inside that route, which are replaced when you start a request.

If you create a client like this

var client = ApiClient.newClient({
    baseUrl: "https://api.example.com/users",
    route: "{id}/{resource}",  
});

and start a request like this

client.get({
    routeParams: {
        id: "5979",  // {id}
        resource: "profile"  // {resource}
    }
});

the client will call the URL

[GET]  https://api.example.com/users/5979/profile

Parameter values can also be functions, what means that the value that is returned by that functions is used as value:

var getUserId = function() : string {
    // load the user ID from somewhere
};

client.get({
    routeParams: {
        id: getUserId,  // {id}
        resource: "profile"  // {resource}
    }
});

A function must have the following structure:

function (paramName: string, routeParams: any, match: string, formatExpr: string, funcDepth: string) : any {
    return <THE-VALUE-TO-USE>;
}

| Name | Description | | ---- | --------- | | paramName | The name of the parameter. For {id} this will be id | | routeParams | The list of submitted route parameters with their values. IMPORTANT: Keep sure to return strings as values! Otherwise you might have problems to convert the values to an URL part. | | match | The complete (unhandled) expression of the argument. | | formatExpr | The optional format expression of the argument. For {id:number} this will be number. | | funcDepth | This value is 0 at the beginning. If you return a function in that function again, this will increase until you stop to return a function. |

Formatting values

Followed by a : char a route parameter definition can additionally contain a "format expression".

These expressions can help you to parse and format parameter values.

The first step to do this is to define a so called "format provider" callback in a client:

client.addFormatProvider((ctx : ApiClient.IFormatProvider) => {
    var toStringSafe = function() : string { 
        return ctx.value ? ctx.value.toString() : "";
    };

    if (ctx.expression === "upper") {    
        ctx.handled = true;
        return toStringSafe().toUpperCase();  // the new value
    }
    else if (ctx.expression === "number") {
        var n = parseInt(toStringSafe().trim());
        if (isNaN(n)) {
            throw "'" + ctx.value + "' is NOT a number!";
        }
        
        ctx.handled = true;
        return n.toString();
    }
});

Here we defined the two expressions upper (convert to upper case chars) and number (keep sure to have a valid number).

To use them you can define a route like this:

{id:number}/{resource:upper}

Now if you setup your client

var client = ApiClient.newClient({
    baseUrl: "https://api.example.com/users",
    route: "{id:number}/{resource:upper}",  
});

and start a request like this

client.get({
    routeParams: {
        id: "5979",
        resource: "profile"
    }
});

the client will call the URL

[GET]  https://api.example.com/users/5979/PROFILE

The ctx object in the format provider call of addFormatProvider() has the following structure:

interface IFormatProviderContext {
    /**
     * Gets the format expression.
     */
    expression: string;
    
    /**
     * Gets if the expression has been handled or not.
     */
    handled: boolean;
    
    /**
     * Gets the underlying (unhandled) value.
     */
    value: any;
}

Authorization

You can submit an optional IAuthorizer object when you start a request:

interface IAuthorizer {
    /**
     * Prepares a HTTP request for authorization.
     * 
     * @param {HTTP.HttpRequestOptions} reqOpts The request options.
     */
    prepare(reqOpts: HTTP.HttpRequestOptions);
}

The plugin provides the following implementations:

AggregateAuthorizer

var authorizer = new ApiClient.AggregateAuthorizer();
authorizer.addAuthorizers(new ApiClient.BasicAuth("Username", "Password"),
                          new ApiClient.BearerAuth("MySecretToken"));

BasicAuth

var authorizer = new ApiClient.BasicAuth("Username", "Password");

BearerAuth

var authorizer = new ApiClient.BearerAuth("MySecretToken");

OAuth

var authorizer = new ApiClient.OAuth("MySecretToken");
authorizer.setField('oauth_field1', 'field1_value');
authorizer.setField('oauth_field2', 'field2_value');

TwitterOAuth

var authorizer = new ApiClient.TwitterOAuth("<CONSUMER_KEY>", "<CONSUMER_SECRET>",
                                            "<TOKEN>", "<TOKEN_SECRET>");

Requests

GET

// ?TM=5979&MK=23979
client.get({
    params: {
        TM: '5979',
        MK: '23979'
    }
});

POST

client.post({
    content: {
        id: 5979,
        name: "Tanja"
    },
    
    type: ApiClient.HttpRequestType.JSON
});

PUT

client.put({
    content: '<user><id>23979</id><name>Marcel</name></user>',
    
    type: ApiClient.HttpRequestType.XML
});

PATCH

client.patch({
    content: '<user id="241279"><name>Julia</name></user>',
    
    type: ApiClient.HttpRequestType.XML
});

DELETE

client.delete({
    content: {
        id: 221286
    },
    
    type: ApiClient.HttpRequestType.JSON
});

Custom

client.request("FOO", {
    content: {
        TM: 5979,
        MK: 23979
    },
    
    type: ApiClient.HttpRequestType.JSON
});

Logging

If you want to log inside your result / error callbacks, you must define one or more logger actions in a client:

var client = ApiClient.newClient({
    baseUrl: "https://example.com/users",
    route: "{id}",  
});

client.addLogger(function(msg : ApiClient.ILogMessage) {
    console.log("[" + ApiClient.LogSource[msg.source] + "]: " + msg.message);
});

Each action receives an object of the following type:

interface ILogMessage {
    /**
     * Gets the category.
     */
    category: LogCategory;
    
    /**
     * Gets the message value.
     */
    message: any;
    
    /**
     * Gets the priority.
     */
    priority: LogPriority;
    
    /**
     * Gets the source.
     */
    source: LogSource;
    
    /**
     * Gets the tag.
     */
    tag: string;
    
    /**
     * Gets the timestamp.
     */
    time: Date;
}

Now you can starts logging in your callbacks:

client.clientError(function(result : ApiClient.IApiClientResult) {
                       result.warn("Client error: " + result.code);
                   })
      .serverError(function(result : ApiClient.IApiClientResult) {
                       result.err("Server error: " + result.code);
                   })
      .success(function(result : ApiClient.IApiClientResult) {
                    result.info("Success: " + result.code);
               })
      .error(function(err : ApiClient.IApiClientError) {
                 result.crit("API CLIENT ERROR!: " + err.error);
             })
      .completed(function(ctx : ApiClient.IApiClientCompleteContext) {
                     result.dbg("Completed action invoked.");
                 });

The IApiClientResult, IApiClientError and IApiClientCompleteContext objects using the ILogger interface:

interface ILogger {
    /**
     * Logs an alert message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    alert(msg : any, tag?: string,
          priority?: LogPriority) : ILogger;
    
    /**
     * Logs a critical message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    crit(msg : any, tag?: string,
         priority?: LogPriority) : ILogger;
    
    /**
     * Logs a debug message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    dbg(msg : any, tag?: string,
        priority?: LogPriority) : ILogger;
    
    /**
     * Logs an emergency message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    emerg(msg : any, tag?: string,
          priority?: LogPriority) : ILogger;
    
    /**
     * Logs an error message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    err(msg : any, tag?: string,
        priority?: LogPriority) : ILogger;
    
    /**
     * Logs an info message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    info(msg : any, tag?: string,
         priority?: LogPriority) : ILogger;
    
    /**
     * Logs a message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogCategory} [category] The optional log category. Default: LogCategory.Debug
     * @param {LogPriority} [priority] The optional log priority.
     */
    log(msg : any, tag?: string,
        category?: LogCategory, priority?: LogPriority) : ILogger;
    
    /**
     * Logs a notice message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    note(msg : any, tag?: string,
         priority?: LogPriority) : ILogger;
     
    /**
     * Logs a trace message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    trace(msg : any, tag?: string,
          priority?: LogPriority) : ILogger;
        
    /**
     * Logs a warning message.
     * 
     * @param any msg The message value.
     * @param {String} [tag] The optional tag value.
     * @param {LogPriority} [priority] The optional log priority.
     */
    warn(msg : any, tag?: string,
         priority?: LogPriority) : ILogger;
}

URL parameters

You can befine additional parameters for the URL.

If you create a client like this

var client = ApiClient.newClient({
    baseUrl: "https://api.example.com/users"
});

and start a request like this

client.get({
    params: {
        id: '23979',
        resource: "profile"
    }
});

The client will call the URL

[GET]  https://api.example.com/users?id=23979&resource=profile

Like route parameters you can also use functions for defining URL parameters:

var getUserId = function() : string {
    // load the user ID from somewhere
};

client.get({
    params: {
        id: getUserId,  // {id}
        resource: "profile"  // {resource}
    }
});

A function must have the following structure:

function (paramName: string, index: number, funcDepth: string) : any {
    return <THE-VALUE-TO-USE>;
}

| Name | Description | | ---- | --------- | | paramName | The name of the parameter. For {id} this will be id | | index | The zero based index of the handled URL parameter. | | funcDepth | This value is 0 at the beginning. If you return a function in that function again, this will increase until you stop to return a function. |

IMPORTANT: It is also recommended to use / return strings a parameter values to prevent problems when converting the values to an URL string.

Responses

Callbacks

Simple

client.success(function(result : ApiClient.IApiClientResult) {
                    // handle any response
               });

The result object has the following structure:

interface IApiClientResult extends ILogger {
    /**
     * Gets the underlying API client.
     */
    client: IApiClient;

    /**
     * Gets the HTTP response code.
     */
    code: number;
    
    /**
     * Gets the raw content.
     */
    content: any;
    
    /**
     * Gets the underlying (execution) context.
     */
    context: ApiClientResultContext;
    
    /**
     * Gets the response headers.
     */
    headers: HTTP.Headers;
    
    /**
     * Returns the content as wrapped AJAX result object.
     * 
     * @return {IAjaxResult<TData>} The ajax result object.
     */
    getAjaxResult<TData>() : IAjaxResult<TData>;
    
    /**
     * Returns the content as file.
     * 
     * @param {String} [destFile] The custom path of the destination file.
     * 
     * @return {FileSystem.File} The file.
     */
    getFile(destFile?: string) : FileSystem.File;
    
    /**
     * Tries result the content as image source.
     */
    getImage(): Promise<Image.ImageSource>;
    
    /**
     * Returns the content as JSON object.
     */
    getJSON<T>() : T;
    
    /**
     * Returns the content as string.
     */
    getString() : string;
    
    /**
     * Gets the information about the request.
     */
    request: IHttpRequest;
    
    /**
     * Gets the raw response.
     */
    response: HTTP.HttpResponse;
}

Errors

client.error(function(err : ApiClient.IApiClientError) {
                 // handle an HTTP client error here
             });

The err object has the following structure:

interface IApiClientError extends ILogger {
    /**
     * Gets the underlying client.
     */
    client: IApiClient;
    
    /**
     * Gets the context.
     */
    context: ApiClientErrorContext;
    
    /**
     * Gets the error data.
     */
    error: any;
    
    /**
     * Gets or sets if error has been handled or not.
     */
    handled: boolean;
    
    /**
     * Gets the information about the request.
     */
    request: IHttpRequest;
}

Conditional callbacks

You can define callbacks for any kind of conditions.

A generic way to do this is to use the if() method:

client.if(function(result : IApiClientResult) : boolean {
              // invoke if 'X-My-Custom-Header' is defined
              return undefined !== result.headers["X-My-Custom-Header"];
          },
          function(result : IApiClientResult) {
              // handle the response
          });

If no condition matches, the callback defined by success() method is used.

For specific status codes you can use the ifStatus() method:

client.ifStatus((statusCode) => statusCode === 500,
                function(result : IApiClientResult) {
                    // handle the internal server error
                });

Or shorter:

client.status(500,
              function(result : IApiClientResult) {
                  // handle the internal server error
              });
Short hand callbacks
client.clientError(function(result : ApiClient.IApiClientResult) {
                       // handle status codes between 400 and 499
                   });
                   
client.ok(function(result : ApiClient.IApiClientResult) {
                       // handle status codes with 200, 204 or 205
                   });
                   
client.serverError(function(result : ApiClient.IApiClientResult) {
                       // handle status codes between 500 and 599
                   });

The following methods are also supported:

| Name | Description | | ---- | --------- | | badGateway | Handles a request with status code 502. | | badRequest | Handles a request with status code 400. | | clientOrServerError | Handles a request with a status code between 400 and 599. | | conflict | Handles a request with status code 409. | | forbidden | Handles a request with status code 403. | | gatewayTimeout | Handles a request with status code 504. | | gone | Handles a request with status code 410. | | informational | Handles a request with a status code between 100 and 199. | | insufficientStorage | Handles a request with status code 507. | | internalServerError | Handles a request with status code 500. | | locked | Handles a request with status code 423. | | methodNotAllowed | Handles a request with status code 405. | | notFound | Handles a request with status code 404. | | notImplemented | Handles a request with status code 501. | | partialContent | Handles a request with status code 206. | | payloadTooLarge | Handles a request with status code 413. | | redirection | Handles a request with a status code between 300 and 399. | | serviceUnavailable | Handles a request with status code 503. | | succeededRequest | Handles a request with a status code between 200 and 299. | | tooManyRequests | Handles a request with status code 429. | | unauthorized | Handles a request with status code 401. | | unsupportedMediaType | Handles a request with status code 415. | | uriTooLong | Handles a request with status code 414. |