A decorator based http webservice client build with typescript (inspired bei feign).
- Handle REST based webservices
- Configure a decoder (defaults to JSON)
- Generic request/response interceptor chain
- Basic authentication
- Request parameters (currently on GET requests)
- Custom headers per method
Install as npm package:
npm install pretend --saveNote: To work on node.js (server-side) the fetch must be polyfilled. This could easy be done importing isomorphic-fetch.
classTest{
@Headers('Accept: application/json')
@Get('/path/{id}',true)publicasyncget(id: string,parameters: any){}
@Post('/path')publicasyncpost(body: any){}
@Post('/path')publicasyncpost(@FormData('name')blob: any){}
@Put('/path')publicasyncput(){}
@Delete('/path/:id')publicasyncdelete(id: string){}}asyncfunctioncall(){constclient=Pretend.builder().target(Test,'http://host:port/');constresult=awaitclient.get('some-id',{'name': 'value'});}// Executes a GET request to 'http://host:port/path/some-id?name=value'call();Decoders, basicAuthentication and requestInterceptors are all special forms of the more generic interceptors which could be chained per request/response.
// Configure a text based decoderconstclient=Pretend.builder().decoder(Pretend.TextDecoder).target(Test,'http://host:port/');// Configure basic authenticationconstclient=Pretend.builder().basicAuthentication('user','pass').target(Test,'http://host:port/');// Configure a request interceptorconstclient=Pretend.builder().requestInterceptor((request)=>{request.options.headers['X-Custom-Header']='value';returnrequest;}).target(Test,'http://host:port/');Multiple interceptors could be added to each builder. The order of interceptor calls will result in a chain of calls like illistrated below:
// Configure a request interceptorconstclient=Pretend.builder().interceptor(async(chain,request)=>{console.log('interceptor 1: request');constresponse=awaitchain(request);console.log('interceptor 1: response');returnresponse;}).interceptor(async(chain,request)=>{console.log('interceptor 2: request');constresponse=awaitchain(request);console.log('interceptor 2: response');returnresponse;}).target(Test,'http://host:port/'); +---------------+ +---------------+
Request ---> | | -> | |
| Interceptor 1 | | Interceptor 2 | -> HTTP REST call
Response <-- | | <- | |
+---------------+ +---------------+
This leads to the following console output:
interceptor 1: request
interceptor 2: request
interceptor 2: response
interceptor 1: response
DataMappers could be used to map response structures to TypeScript classes.
This is done using the @ResponseType decorator.
classUser{publicname: string;constuctor(data: {name: string}){this.name=data.name;}}classAPI{
@Get('/path/{id}')
@ResponseType(User)publicasyncloadUser(id: string): Promise<User>{/* * `/path/{id}` returns a JSON like this from the server: * * { * name: 'some string' * } */}}constclient=Pretend.builder().target(API,'http://host:port/');constresult: User=awaitclient.loadUser(1);There is a second parameter to the @ResponseType decorator which is a transform function.
The input is the server response, the output need to match the class constructor parameters.
Note: The constructor parameters are always an array!
classUser{publicgetname(): string{returnthis.data.name;}constuctor(privatedata: {name: string}){}}classAPI{
@Get('/path/{id}')
@ResponseType(User,(data)=>[{name: `${data.firstname}${data.lastname}`}])publicasyncloadUser(id: string): Promise<User>{/* * `/path/{id}` returns a JSON like this from the server: * * { * firstname: 'John', * lastname: 'Doe' * } */}}constclient=Pretend.builder().target(API,'http://host:port/');constresult: User=awaitclient.loadUser(1);- Named parameters