18.3.2 • Published 2 months ago

@airma/restful v18.3.2

Weekly downloads
-
License
-
Repository
github
Last release
2 months ago

npm NPM downloads standard

@airma/restful

Basic usage

Provided a nice way to describe a restful http requests like:

import { client } from '@airma/restful';

const { rest } = client();

const root = rest('/path');

// GET http://host/path
root.get();

// GET http://host/path?param1=param1&param2=param2
root.setParams({ param1:'param1', param2:'param2' }).get();

// GET http://host/path/child-path
root.path('child-path').get();

// GET http://host/path/child-path?param1=param1&param2=param2
root.path('child-path').setParams({ param1:'param1', param2:'param2' }).get();

// POST http://host/path 
// payload: {data:'data'}
root.setBody({data:'data'}).post();

// POST http://host/path/child-path 
// payload: {data:'data'}
root.path('child-path').setBody({data:'data'}).post();

// POST http://host/path/child-path?param1=param1&param2=param2 
// payload: {data:'data'}
root.path('child-path').setParams({ param1:'param1', param2:'param2' }).setBody({data:'data'}).post();

With typescript

import { client, ResponseData } from '@airma/restful';

const { rest } = client();

const root = rest('/path');

type User = {
    id: string;
    name: string;
    username: string;
}

async function getUser(id: string): Promise<User>{
    try{
        return root.setParams({ id }).get<User>();
    } catch(e: any) {
        console.log(e)
    }
}

// if you need details from response, use `response` from `method promise`
async function getUserResponse(id: string): Promise<ResponseData<User>>{
    try{
        // with response
        // when error { isError: true; error: any, networkError: boolean, status: number, headers?: Record<string, any> }
        // when success { isError: false; data: User, status: number, headers: Record<string, any> }
        return root.setParams({ id }).get<User>().response();
    } catch(e: any) {
        console.log(e)
    }
}

Config

The @airma/restful contains a simple default config, it is not enough for you, you can config it:

import { client, ResponseData } from '@airma/restful';

const { rest } = client({
    // request headers
    headers: {...},
    // default params ex { sessionId: 12 } -> http://host/path?sessionId=12&xx=...
    defaultParams: {...},
    // intercept the response data
    responseInterceptor: ( data: ResponseData ) => {console.log(data)},
    // you can replace the default request with your own request method
    async request(url: string, requestConfig: RequestConfig){
        return Promise<ResponseData>;
    }
});

Headers

You can set a headers for the client, and the client always sends request with the headers you set.

client({
    headers:{
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
        ......
    }
})

DefaultParams

You can set default params for the requests send by you client. These params always exist in the url, unless you recover them with undefined in the specific request.

const {rest} = client({
    defaultParams:{
        sessionId: 12,
        private:true
    }
});

// http://host/path/child?sessionId=12&private=true&userId=1
rest('/path').path('child').setParams({userId:1}).get<User>();

// http://host/path/child?sessionId=12&userId=1
rest('/path').path('child').setParams({userId:1, private: undefined}).get<User>();

Request

The default request sender is a window.fetch adapter. If you want to replace it with some more good adapter, you can set request.

The Request is a callback, with a url and requestConfig as params, it should returns a promise with a ResponseData type result.

type Request = (
  // request url  
  url: string,
  // requset config
  requestConfig: RequestConfig
) => Promise<ResponseData>; // response data

The RequestConfig type:

type ResponseType =
  | 'json'
  | 'text'
  | 'formData'
  | 'blob'
  | 'arrayBuffer';

// the config you can set to `client`
type RestConfig = {
    // just headers you set to client({headers:...})
  headers?: Record<string, any>;
  // the response data format: json, text, formData, blob, arrayBuffer
  responseType?: ResponseType;
  // just responseInterceptor you set to client({responseInterceptor})
  responseInterceptor?: (
    data: ResponseData
  ) => ResponseData | undefined;
  // just defaultParams you set to client({defaultParams...})
  defaultParams?: Record<string | number, any>;
  // you can provide a param processor to stringify and parse
  // params.
  // type ParamsProcessor = { 
  //   stringify(data:Record<string | number, any>): string,
  //   parse(data: string): Record<string | number, any>
  // }
  paramsProcessor?: () => ParamsProcessor;
  /** the rest config you can refer to window.fetch API **/
  credentials?: 'include' | 'omit' | 'same-origin';
  cache?:
    | 'default'
    | 'force-cache'
    | 'no-cache'
    | 'no-store'
    | 'only-if-cached'
    | 'reload';
  mode?: 'cors' | 'navigate' | 'no-cors' | 'same-origin';
  redirect?: 'error' | 'follow' | 'manual';
  integrity?: string;
  keepalive?: boolean;
  referrer?: string;
  referrerPolicy?:
    | ''
    | 'no-referrer'
    | 'no-referrer-when-downgrade'
    | 'origin'
    | 'origin-when-cross-origin'
    | 'same-origin'
    | 'strict-origin'
    | 'strict-origin-when-cross-origin'
    | 'unsafe-url';
  window?: null;
}

type RequestConfig = RestConfig & {
  // object style for ?param=xxx&more=xxx
  params?: Record<string, any>;
  // object style for post request payload
  body?: Record<string, any>;
  // GET, POST, PUT, DELETE
  method?: Method;
};

The ResponseData type:

export declare type SuccessResponse<T = any> = {
  status: number;
  headers?: Record<string, any>;
  // the final promise result
  data: T;
  // is error request
  isError: false;
};

export declare type ErrorResponse = {
  status: number | null;
  // error data
  error: any;
  // same value with `error`
  data: any;
  headers?: Record<string, any>;
  // is network error
  networkError: boolean;
  // is error request
  isError: true;
};

export declare type ResponseData<T = any> = SuccessResponse<T> | ErrorResponse;

You can replace request with the most popular request tool axios or other request API. And now you should know, that @airma/restful just provides a restful style for you.

ResponseInterceptor

ResponseInterceptor is a callback which helps you intercept a response. You can redefine if the request is an error request, and affect if the rest end should resolve or reject this result. You can also just make a log, and don't change result.

intercept response:

import {client, ResponseData} from '@airma/restful';

// for example we have to judge if the request is success 
// by the `data.success`
const redefinition = (response:ResponseData) :ResponseData =>{
    const {data, status, networkError} = response;
    if ( data && data.success ) {
        return {
            data: data.data,
            status,
            isError: false
        }
    }else {
        return {
            error: data.data,
            data: data.data,
            status,
            networkError,
            isError: true
        }
    }
}

const { rest } = client({
    responseInterceptor: redefinition
})

log response:

import {client, ResponseData} from '@airma/restful';

// the different with redefinition is we should return void,
// if we do not want to change result.
const log = (response:ResponseData): void =>{
    const {data, status} = response;
    console.log(status, data);
}

const { rest } = client({
    responseInterceptor: log
})

paramsProcessor

If you want to keep the default request, but replace a better params processor like qs, you can use this setting.

import { client } from '@airma/restful';
import qs from 'qs';

export default client({
    paramsProcessor(){
        return {
            stringify(data: Record<string | number, any>): string{
                return qs.stringify(data);
            },
            parse(query: string): Record<string | number, any>{
                return qs.parse(query);
            }
        }
    }
})

Change config

Sometimes you want to change rest config when the client has been built. You can use config method from client instance.

import {client} from '@airma/restful';

const {rest, config} = client();

async function test(){
    // http://host/path?id=1
    await rest('/path').setParams({id:1}).get();
    // set a sessionId as a default param
    config(
        c => ({...c, defaultParams: {sessionId: 12} })
    );
    // http://host/path?sessionId=12&id=1
    await rest('/path').setParams({id:1}).get();
}

You can set headers and other config too.

If you want to config a specific request, you should set config to a method.

rest('/path').get({headers:{...}, responseType:'text'});

Response

As we have known, the method get, post, put, delete provides a promise which has a result we need directly. But, sometimes, we need a more original response data with status, headers, networkError properties. So, we can use response from the method returns.

const res = await rest('/path').get<User>().response();
if(!res.isError){
    return res.data; // User type
}
// we can get details now
const {error, networkError, headers} = res;

The response method is a parasitic method in the get, post, put, delete method returning promise. It returns a promise which result is a ResponseData.

18.3.2

2 months ago

18.3.1

2 months ago

18.3.0

2 months ago

15.2.0

10 months ago

15.2.1

10 months ago

15.2.2

10 months ago

15.2.3

10 months ago

15.1.5

1 year ago

15.1.6

1 year ago

15.1.4

1 year ago

15.1.3

1 year ago

15.1.2

1 year ago

15.1.1

1 year ago

15.1.0

1 year ago

15.0.1

1 year ago

15.0.0

1 year ago