@servicestack/client
    TypeScript icon, indicating that this package has built-in type declarations

    1.1.17 • Public • Published

    @servicestack/client

    This library contains the Typed ServiceStack Client library that is an idiomatic port of ServiceStack's ss-utils.js JavaScript Client in native TypeScript. It provides integration with many ServiceStack features including TypeScript Add ServiceStack Reference and Server Events.

    Web, Node.js and React Native

    It contains a clean "jQuery-free" implementation based on JavaScript's new Fetch API standard, utilizing the fetch-everywhere implementation so it can be used in both JavaScript Web apps, Node.js server and test projects as well as React Native iOS and Android Mobile Apps as seen in TypeScript Server Event Examples.

    Install

    This package is pre-configured in all ServiceStackVS TypeScript VS.NET Templates or can be installed with:

    npm install @servicestack/client
    

    v1.x Changes

    • new api and apiVoid APIs return an ApiResult<TResponse> encapsulating both response and error
    • old send API renamed to fetch
    • new send and sendVoid APIs can infer IVerb Interface Markers

    New API

    The new api returns a typed ApiResult<Response> Value Result that encapsulates either a Typed Response or a structured API Error populated in ResponseStatus allowing you to handle API responses programmatically without try/catch handling:

    const api = client.api(new Hello({ name }))
    if (api.failed) {
        console.log(`Greeting failed! ${e.errorCode}: ${e.errorMessage}`);
        return;
    }
    
    console.log(`API Says: ${api.response.result}`) //api.succeeded

    Simplified API Handling

    Being able to treat errors as values greatly increases the ability to programmatically handle and genericise api handling and greatly simplifies functionality needing to handle both successful and error responses like binding to UI components.

    An example of this is below where we're able to concurrently fire off multiple unrelated async requests in parallel, wait for them all to complete, print out the ones that have succeeded or failed then access their strong typed responses:

    import { JsonServiceClient } from "@servicestack/client"
    
    let requests:ApiRequest[] = [
        new AppOverview(),            // GET => AppOverviewResponse
        new DeleteTechnology(),       // DELETE => IReturnVoid (requires auth) 
        new GetAllTechnologies(),     // GET => GetAllTechnologiesResponse
        new GetAllTechnologyStacks(), // GET => GetAllTechnologyStacksResponse
    ]
    
    let results = await Promise.all(requests.map(async (request) =>
        ({ request, api:await client.api(request) as ApiResponse}) ))
    
    let failed = results.filter(x => x.api.failed)
    console.log(`${failed.length} failed:`)
    failed.forEach(x =>
        console.log(`    ${x.request.getTypeName()} Request Failed: ${failed.map(x => x.api.errorMessage)}`))
    
    let succeeded = results.filter(x => x.api.succeeded)
    console.log(`\n${succeeded.length} succeeded: ${succeeded.map(x => x.request.getTypeName()).join(', ')}`)
    
    let r = succeeded.find(x => x.request.getTypeName() == 'AppOverview')?.api.response as AppOverviewResponse
    if (r) console.log(`Top 5 Technologies: ${r.topTechnologies.slice(0,4).map(tech => tech.name).join(', ')}`)

    Output:

    1 failed
    DeleteTechnology Request Failed: Unauthorized
    
    3 succeeded: AppOverview, GetAllTechnologies, GetAllTechnologyStacks
    Top 5 Technologies: Redis, MySQL, Amazon EC2, Nginx
    

    Being able to treat Errors as values has dramatically reduced the effort required to accomplish the same feat if needing to handle errors with try/catch.

    Requires ServiceStack v5.13+

    Ideal Typed Message-based API

    The TypeScript JsonServiceClient enables the same productive, typed API development experience available in ServiceStack's other 1st-class supported client platforms.

    The JsonServiceClient leverages the additional type hints ServiceStack embeds in each TypeScript Request DTO to achieve the ideal typed, message-based API - so all API requests benefit from a succinct, boilerplate-free Typed API.

    Here's a quick look at what it looks like. The example below shows how to create a C# Gist in Gistlyn after adding a TypeScript ServiceStack Reference to gistlyn.com and installing the @servicestack/client npm package:

    import { JsonServiceClient } from '@servicestack/client';
    import { StoreGist, GithubFile } from './dtos';
    
    const client = new JsonServiceClient("https://gistlyn.com");
    
    const request = new StoreGist({
        files: { 
            [file.filename]: new GithubFile({
                filename: 'main.cs',
                content: 'var greeting = "Hi, from TypeScript!";' 
            }) 
        }
    })
    
    const api = client.api(request); //response:StoreGistResponse
    if (api.succeeded) {
        console.log(`New C# Gist was created with id: ${r.gist}`);
        location.href = `https://gist.cafe/${r.gist}`;
    } else {
        console.log("Failed to create Gist: ", e.errorMessage);
    }

    Where the response param is typed to StoreGistResponse DTO Type.

    Sending additional arguments with Typed API Requests

    Many AutoQuery Services utilize implicit conventions to query fields that aren't explicitly defined on AutoQuery Request DTOs, these can now be queried by specifying additional arguments with the typed Request DTO, e.g:

    //typed to QueryResponse<TechnologyStack> 
    const response = await client.get(new FindTechStacks(), { VendorName: "ServiceStack" });

    Which will return TechStacks developed by ServiceStack.

    Calling APIs with Custom URLs

    You can call Services using relative or absolute urls, e.g:

    client.get<GetTechnologyResponse>("/technology/ServiceStack")
    
    client.get<GetTechnologyResponse>("http://techstacks.io/technology/ServiceStack")
    
    // GET http://techstacks.io/technology?Slug=ServiceStack
    client.get<GetTechnologyResponse>("/technology", { Slug: "ServiceStack" }) 

    as well as POST Request DTOs to custom urls:

    client.postToUrl("/custom-path", request, { Slug: "ServiceStack" });
    
    client.putToUrl("http://example.org/custom-path", request);

    Raw Data Responses

    The JsonServiceClient also supports Raw Data responses like string and byte[] which also get a Typed API once declared on Request DTOs using the IReturn<T> marker:

    public class ReturnString : IReturn<string> {}
    public class ReturnBytes : IReturn<byte[]> {}

    Which can then be accessed as normal, with their Response typed to a JavaScript string or Uint8Array for raw byte[] responses:

    let str:string = await client.get(new ReturnString());
    
    let data:Uint8Array = await client.get(new ReturnBytes());

    Authenticating using Basic Auth

    Basic Auth support is implemented in JsonServiceClient and follows the same API made available in the C# Service Clients where the userName/password properties can be set individually, e.g:

    var client = new JsonServiceClient(baseUrl);
    client.userName = user;
    client.password = pass;
    
    const response = await client.get(new SecureRequest());

    Or use client.setCredentials() to have them set both together.

    Authenticating using Credentials

    Alternatively you can authenticate using userName/password credentials by adding a TypeScript Reference to your remote ServiceStack Instance and sending a populated Authenticate Request DTO, e.g:

    const response = await client.post(new Authenticate({
        provider: "credentials", userName, password, rememberMe: true }));

    This will populate the JsonServiceClient with Session Cookies which will transparently be sent on subsequent requests to make authenticated requests.

    Authenticating using JWT

    Use the bearerToken property to Authenticate with a ServiceStack JWT Provider using a JWT Token:

    client.bearerToken = jwtToken;

    Alternatively you can use a Refresh Token instead:

    client.refreshToken = refreshToken;

    Authenticating using an API Key

    Use the bearerToken property to Authenticate with an API Key:

    client.bearerToken = apiKey;

    Transparently handle 401 Unauthorized Responses

    If the server returns a 401 Unauthorized Response either because the client was Unauthenticated or the configured Bearer Token or API Key used had expired or was invalidated, you can use onAuthenticationRequired callback to re-configure the client before automatically retrying the original request, e.g:

    client.onAuthenticationRequired = async () => {
        const authClient = new JsonServiceClient(authBaseUrl);
        authClient.userName = userName;
        authClient.password = password;
        const response = await authClient.get(new Authenticate());
        client.bearerToken = response.bearerToken;
    };
    
    //Automatically retries requests returning 401 Responses with new bearerToken
    var response = await client.get(new Secured());

    Automatically refresh Access Tokens

    With the Refresh Token support in JWT you can use the refreshToken property to instruct the Service Client to automatically fetch new JWT Tokens behind the scenes before automatically retrying failed requests due to invalid or expired JWTs, e.g:

    //Authenticate to get new Refresh Token
    const authClient = new JsonServiceClient(authBaseUrl);
    authClient.userName = userName;
    authClient.password = password;
    const authResponse = await authClient.get(new Authenticate());
    
    //Configure client with RefreshToken
    client.refreshToken = authResponse.RefreshToken;
    
    //Call authenticated Services and clients will automatically retrieve new JWT Tokens as needed
    const response = await client.get(new Secured());

    Use the refreshTokenUri property when refresh tokens need to be sent to a different ServiceStack Server, e.g:

    client.refreshToken = refreshToken;
    client.refreshTokenUri = authBaseUrl + "/access-token";

    ServerEvents Client

    The TypeScript ServerEventClient is an idiomatic port of ServiceStack's C# Server Events Client in native TypeScript providing a productive client to consume ServiceStack's real-time Server Events that can be used in TypeScript Web, Node.js Server and React Native iOS and Android Mobile Apps.

    const channels = ["home"];
    const client = new ServerEventsClient("/", channels, {
        handlers: {
            onConnect: (sub:ServerEventConnect) => {  // Successful SSE connection
                console.log("You've connected! welcome " + sub.displayName);
            },
            onJoin: (msg:ServerEventJoin) => {        // User has joined subscribed channel
                console.log("Welcome, " + msg.displayName);
            },
            onLeave: (msg:ServerEventLeave) => {      // User has left subscribed channel
                console.log(msg.displayName + " has left the building");
            },
            onUpdate: (msg:ServerEventUpdate) => {    // User channel subscription was changed
                console.log(msg.displayName + " channels subscription were updated");
            },        
            onMessage: (msg:ServerEventMessage) => {},// Invoked for each other message
            //... Register custom handlers
            announce: (text:string) => {},            // Handle messages with simple argument
            chat: (chatMsg:ChatMessage) => {},        // Handle messages with complex type argument
            CustomMessage: (msg:CustomMessage) => {}, // Handle complex types with default selector
        },
        receivers: { 
            //... Register any receivers
            tv: {
                watch: function (id) {                // Handle 'tv.watch {url}' messages 
                    var el = document.querySelector("#tv");
                    if (id.indexOf('youtu.be') >= 0) {
                        var v = splitOnLast(id, '/')[1];
                        el.innerHTML = templates.youtube.replace("{id}", v);
                    } else {
                        el.innerHTML = templates.generic.replace("{id}", id);
                    }
                    el.style.display = 'block'; 
                },
                off: function () {                    // Handle 'tv.off' messages
                    var el = document.querySelector("#tv");
                    el.style.display = 'none';
                    el.innerHTML = '';
                }
            }
        },
        onException: (e:Error) => {},                 // Invoked on each Error
        onReconnect: (e:Error) => {}                  // Invoked after each auto-reconnect
    })
    .addListener("theEvent",(e:ServerEventMessage) => {}) // Add listener for pub/sub event trigger
    .start();                                             // Start listening for Server Events!

    When publishing a DTO Type for your Server Events message, your clients will be able to benefit from the generated DTOs in TypeScript ServiceStack References.

    Feedback and Support

    Support is available from the ServiceStack Customer Forums or StackOverflow using the [servicestack] tag.

    Any reproducible issues can be submitted to ServiceStack's Issue Tracker.

    Troubleshooting

    If you're getting missing Type Definitions for Headers, Response, Request, etc. You'll need to import the Type Definitions for W3C's fetch API, preferably by using the latest version of TypeScript and referencing the core es2016 and dom libs in TypeScript's tsconfig.json, e.g:

    {
      "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "lib": [ "es2015", "dom" ]
      }
    }

    Alternatively you can import the whatwg-fetch Type Definitions with:

    npm install @types/whatwg-fetch --save-dev
    

    Install

    npm i @servicestack/client

    DownloadsWeekly Downloads

    1,013

    Version

    1.1.17

    License

    BSD-3-Clause

    Unpacked Size

    364 kB

    Total Files

    7

    Last publish

    Collaborators

    • mythz