3.0.0 • Published 8 years ago

nativescript-apiclient v3.0.0

Weekly downloads
17
License
MIT
Repository
github
Last release
8 years ago

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>;
}
NameDescription
paramNameThe name of the parameter. For {id} this will be id
routeParamsThe 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.
matchThe complete (unhandled) expression of the argument.
formatExprThe optional format expression of the argument. For {id:number} this will be number.
funcDepthThis 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>;
}
NameDescription
paramNameThe name of the parameter. For {id} this will be id
indexThe zero based index of the handled URL parameter.
funcDepthThis 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:

NameDescription
badGatewayHandles a request with status code 502.
badRequestHandles a request with status code 400.
clientOrServerErrorHandles a request with a status code between 400 and 599.
conflictHandles a request with status code 409.
forbiddenHandles a request with status code 403.
gatewayTimeoutHandles a request with status code 504.
goneHandles a request with status code 410.
informationalHandles a request with a status code between 100 and 199.
insufficientStorageHandles a request with status code 507.
internalServerErrorHandles a request with status code 500.
lockedHandles a request with status code 423.
methodNotAllowedHandles a request with status code 405.
notFoundHandles a request with status code 404.
notImplementedHandles a request with status code 501.
partialContentHandles a request with status code 206.
payloadTooLargeHandles a request with status code 413.
redirectionHandles a request with a status code between 300 and 399.
serviceUnavailableHandles a request with status code 503.
succeededRequestHandles a request with a status code between 200 and 299.
tooManyRequestsHandles a request with status code 429.
unauthorizedHandles a request with status code 401.
unsupportedMediaTypeHandles a request with status code 415.
uriTooLongHandles a request with status code 414.
3.0.0

8 years ago

1.13.0

8 years ago

1.12.9

8 years ago

1.12.8

8 years ago

1.12.7

8 years ago

1.12.6

8 years ago

1.12.5

8 years ago

1.12.4

9 years ago

1.12.3

9 years ago

1.12.1

9 years ago

1.12.0

9 years ago

1.11.7

9 years ago

1.11.6

9 years ago

1.11.5

9 years ago

1.11.4

9 years ago

1.11.3

9 years ago

1.11.2

9 years ago

1.11.1

9 years ago

1.11.0

9 years ago

1.10.0

9 years ago

1.9.0

9 years ago

1.8.0

9 years ago

1.7.0

9 years ago

1.6.7

9 years ago

1.6.6

9 years ago

1.6.5

9 years ago

1.6.4

9 years ago

1.6.3

9 years ago

1.6.2

9 years ago

1.6.1

9 years ago

1.6.0

9 years ago

1.5.2

9 years ago

1.5.1

9 years ago

1.5.0

9 years ago

1.4.0

9 years ago

1.3.0

9 years ago

1.2.8

9 years ago

1.2.7

9 years ago

1.2.6

9 years ago

1.2.5

9 years ago

1.2.4

9 years ago

1.2.2

9 years ago

1.2.1

9 years ago

1.2.0

9 years ago