ts-retrofit
Advanced tools
Comparing version 1.11.0 to 1.11.1
@@ -26,2 +26,3 @@ "use strict"; | ||
const dataResolver_1 = require("./dataResolver"); | ||
const util_1 = require("./util"); | ||
axios_1.default.defaults.withCredentials = true; | ||
@@ -105,3 +106,3 @@ const NON_HTTP_REQUEST_PROPERTY_NAME = "__nonHTTPRequestMethod__"; | ||
const data = this._resolveData(methodName, headers, args); | ||
if (headers["content-type"] && headers["content-type"].indexOf("multipart/form-data") !== -1) { | ||
if (headers["content-type"] && headers["content-type"].indexOf("multipart/form-data") !== -1 && util_1.isNode) { | ||
headers = Object.assign(Object.assign({}, headers), data.getHeaders()); | ||
@@ -108,0 +109,0 @@ } |
{ | ||
"name": "ts-retrofit", | ||
"version": "1.11.0", | ||
"version": "1.11.1", | ||
"description": "A declarative and axios based retrofit implementation for JavaScript and TypeScript.", | ||
@@ -5,0 +5,0 @@ "repository": "https://github.com/nullcc/ts-retrofit", |
@@ -10,3 +10,3 @@ # ts-retrofit | ||
> A declarative and axios based retrofit implementation for JavaScript and TypeScript. | ||
> A declarative and [axios](https://github.com/axios/axios) based retrofit implementation for JavaScript and TypeScript. | ||
@@ -21,156 +21,561 @@ ## Install | ||
Here is a typical service definition and usage: | ||
```typescript | ||
import { | ||
GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, BasePath, Header, Queries, Query, | ||
Headers, Path, QueryMap, Body, FormUrlEncoded, Field, FieldMap, | ||
BaseService, ServiceBuilder, Response | ||
} from "ts-retrofit"; | ||
export const TEST_SERVER_HOST = "http://localhost"; | ||
export const TEST_SERVER_PORT = 12345; | ||
export const TEST_SERVER_ENDPOINT = `${TEST_SERVER_HOST}:${TEST_SERVER_PORT}`; | ||
export const API_PREFIX = "/api/v1"; | ||
export const TOKEN = "abcdef123456"; | ||
import { GET, POST, PUT, PATCH, DELETE, BasePath, Header, Path, Body, BaseService, ServiceBuilder, Response } from "ts-retrofit"; | ||
export interface IUser { | ||
interface User { | ||
id?: number; | ||
name: string; | ||
age: number; | ||
[x: string]: any; | ||
email: string; | ||
} | ||
export interface ISearchQuery { | ||
title?: string; | ||
author?: string; | ||
category?: string; | ||
} | ||
export interface IAuth { | ||
username: string; | ||
password: string; | ||
} | ||
export interface IPost { | ||
title: string; | ||
content: string; | ||
} | ||
@BasePath(API_PREFIX) | ||
export class UserService extends BaseService { | ||
@BasePath("/api/v1") | ||
class UserService extends BaseService { | ||
@GET("/users") | ||
async getUsers(@Header("X-Token") token: string): Promise<Response> { return <Response> {} }; | ||
async getUsers(@Header("Authorization") authorization: string): Promise<Response<Array<User>>> { return <Response<Array<User>>> {} }; | ||
@GET("/users/{userId}") | ||
async getUser(@Header("X-Token") token: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
async getUser(@Header("Authorization") authorization: string, @Path("userId") userId: number): Promise<Response<User>> { return <Response<User>> {} }; | ||
@POST("/users") | ||
async createUser(@Header("X-Token") token: string, @Body user: IUser): Promise<Response> { return <Response> {} }; | ||
async createUser(@Header("Authorization") authorization: string, @Body user: User): Promise<Response> { return <Response> {} }; | ||
@PUT("/users/{userId}") | ||
async replaceUser(@Header("X-Token") token: string, @Path("userId") userId: number, @Body user: IUser): Promise<Response> { return <Response> {} }; | ||
async updateUser(@Header("Authorization") authorization: string, @Path("userId") userId: number, @Body user: User): Promise<Response> { return <Response> {} }; | ||
@PATCH("/users/{userId}") | ||
async updateUser(@Header("X-Token") token: string, @Path("userId") userId: number, @Body user: Partial<IUser>): Promise<Response> { return <Response> {} }; | ||
async patchUser(@Header("Authorization") authorization: string, @Path("userId") userId: number, @Body user: Partial<User>): Promise<Response> { return <Response> {} }; | ||
@DELETE("/users/{userId}") | ||
async deleteUser(@Header("X-Token") token: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
async deleteUser(@Header("Authorization") authorization: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
} | ||
@HEAD("/users/{userId}") | ||
async headUser(@Header("X-Token") token: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
(async () => { | ||
const authorization = "foobar"; | ||
const userService = new ServiceBuilder() | ||
.setEndpoint("https://www.your-host.com") | ||
.build(UserService); | ||
const response = await userService.getUsers(authorization); | ||
// Now you can get response data from response.data | ||
})() | ||
``` | ||
@OPTIONS("/users/{userId}") | ||
async optionsUser(@Header("X-Token") token: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
You can see [test file](test/ts-retrofit.test.ts) to get more examples. | ||
## ServiceBuilder | ||
Example: | ||
```typescript | ||
import { AxiosRequestConfig } from "axios"; | ||
import { | ||
ServiceBuilder, | ||
ResponseInterceptorFunction, | ||
RequestInterceptorFunction, | ||
RequestInterceptor, | ||
ResponseInterceptor, | ||
} from "ts-retrofit"; | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
@GET("/items") | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
@BasePath(API_PREFIX) | ||
export class SearchService extends BaseService { | ||
@GET("/search") | ||
async search(@Header("X-Token") token: string, @QueryMap query: ISearchQuery): Promise<Response> { return <Response> {} }; | ||
const RequestInterceptor: RequestInterceptorFunction = (config) => { | ||
console.log("Before sending request to server."); | ||
return config; | ||
}; | ||
const ResponseInterceptor: ResponseInterceptorFunction = (response) => { | ||
console.log("After receiving response from server."); | ||
return response; | ||
}; | ||
(async () => { | ||
const itemService = new ServiceBuilder() | ||
.setEndpoint(${ENDPOINT}) | ||
.setStandalone(true) | ||
.setRequestInterceptors(RequestInterceptor) | ||
.setResponseInterceptors(ResponseInterceptor) | ||
.build(ItemService); | ||
const response: any = await itemService.getVersion(); | ||
console.log(response.data); | ||
})(); | ||
// outputs: | ||
// Before sending request to server. | ||
// After receiving response from server. | ||
// <response data> | ||
``` | ||
## Decorators | ||
### BasePath | ||
* Position: Class | ||
`BasePath` decorator declares the path prefix of a service. | ||
```typescript | ||
// The common path of ItemService is ${ENDPOINT}/api/v1 | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService {} | ||
``` | ||
### HTTP Method Decorators | ||
All HTTP method decorators have an optional second parameter with type **HttpMethodOptions**: | ||
```typescript | ||
export interface HttpMethodOptions { | ||
ignoreBasePath?: boolean; | ||
} | ||
``` | ||
#### GET | ||
* Position: Method | ||
`GET` decorator declares that what it decorated use HTTP **GET** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
@GET("/items") | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
// GET ${ENDPOINT}/items | ||
@GET("/items", { ignoreBasePath: true }) | ||
async getItemsWithoutBasePath(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
#### POST | ||
* Position: Method | ||
`POST` decorator declares that what it decorated use HTTP **POST** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// POST ${ENDPOINT}/api/v1/items | ||
@POST("/items") | ||
async createItem(@Body item: Item): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### PUT | ||
* Position: Method | ||
`PUT` decorator declares that what it decorated use HTTP **PUT** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// PUT ${ENDPOINT}/api/v1/items/{itemId} | ||
@PUT("/items/{itemId}") | ||
async updateItem(@Path("itemId") itemId: number, @Body item: Item): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### PATCH | ||
* Position: Method | ||
`PATCH` decorator declares that what it decorated use HTTP **PATCH** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// PATCH ${ENDPOINT}/api/v1/items/{itemId} | ||
@PATCH("/items/{itemId}") | ||
async patchItem(@Path("itemId") itemId: number, @Body item: Partial<Item>): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### DELETE | ||
* Position: Method | ||
`DELETE` decorator declares that what it decorated use HTTP **DELETE** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// DELETE ${ENDPOINT}/api/v1/items/{itemId} | ||
@DELETE("/items/{itemId}") | ||
async deleteItem(@Path("itemId") itemId: number): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### HEAD | ||
* Position: Method | ||
`HEAD` decorator declares that what it decorated use HTTP **HEAD** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class FileService extends BaseService { | ||
// HEAD ${ENDPOINT}/api/v1/files/{fileId} | ||
@HEAD("/files/{fileId}") | ||
async getFileMetaInfo(@Path("fileId") fileId: number): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### OPTIONS | ||
* Position: Method | ||
`OPTIONS` decorator declares that what it decorated use HTTP **OPTIONS** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// OPTIONS ${ENDPOINT}/api/v1/items/{itemId} | ||
@OPTIONS("/items/{itemId}") | ||
async getFileMetaInfo(@Path("itemId") itemId: number): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
### Headers | ||
* Position: Method | ||
`Headers` decorator declares that what **static HTTP headers** should be added to request. | ||
```typescript | ||
@BasePath("") | ||
export class AuthService extends BaseService { | ||
@POST("/oauth2/authorize") | ||
@POST("/oauth/token") | ||
@Headers({ | ||
"content-type": "application/x-www-form-urlencoded", | ||
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8", | ||
"Accept": "application/json" | ||
}) | ||
async auth(@Body body: IAuth): Promise<Response> { return <Response> {} }; | ||
async auth(@Body body: OAuth): Promise<Response<Token>> { return <Response<Token>>{} }; | ||
} | ||
``` | ||
@BasePath(API_PREFIX) | ||
export class PostService extends BaseService { | ||
@GET("/posts") | ||
### Header | ||
* Position: Method Parameter | ||
`Header` decorator parameterizes the header in HTTP request. Client can provide a value to **one header**. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
async getItems(@Header("Authorization") authorization: string): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### HeaderMap | ||
- Position: Method Parameter | ||
`HeaderMap` decorator parameterizes the headers in HTTP request. Client can provide values to **multi headers**. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
async getItems(@HeaderMap headers: any): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### Path | ||
- Position: Method Parameter | ||
`Path` decorator parameterizes the part of path in HTTP request. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items/{itemId} | ||
@GET("/items/{itemId}") | ||
async getItem(@Path("itemId") itemId: number): Promise<Response<Item>> { return <Response<Item>> {} }; | ||
} | ||
``` | ||
### Body | ||
- Position: Method Parameter | ||
`Body` decorator parameterizes the body in HTTP request. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// POST ${ENDPOINT}/api/v1/items | ||
@POST("/items") | ||
async createItem(@Body item: Item): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
### Queries | ||
- Position: Method | ||
`Queries` decorator declares that what **static queries** should be added to request. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items?size=20 | ||
@GET("/items") | ||
@Queries({ | ||
page: 1, | ||
size: 20, | ||
sort: "createdAt:desc", | ||
}) | ||
async getPosts(): Promise<Response> { return <Response> {} }; | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
@GET("/posts") | ||
### Query | ||
- Position: Method Parameter | ||
`Query` decorator parameterizes the query in HTTP request. Client can provide a value to **one query parameter**. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items?size=20 | ||
@GET("/items") | ||
@Queries({ | ||
page: 1, | ||
size: 20, | ||
sort: "createdAt:desc", | ||
}) | ||
async getPosts1(@Query('group') group: string): Promise<Response> { return <Response> {} }; | ||
@POST("/posts") | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### QueryMap | ||
- Position: Method Parameter | ||
`QueryMap` decorator parameterizes the queries in HTTP request. Client can provide values to **multi queries**. | ||
```typescript | ||
@BasePath("") | ||
class SearchService extends BaseService { | ||
// GET ${ENDPOINT}/search?a=foo&b=bar | ||
@GET("/search") | ||
async search(@QueryMap query: SearchQuery): Promise<Response<SearchResult>> { return <Response<SearchResult>> {} }; | ||
} | ||
``` | ||
### FormUrlEncoded | ||
- Position: Method | ||
`FormUrlEncoded` declares that the content type is **application/x-www-form-urlencoded;charset=utf-8** in HTTP request. | ||
```typescript | ||
@BasePath("") | ||
export class AuthService extends BaseService { | ||
@POST("/oauth/token") | ||
@FormUrlEncoded | ||
async createPost(@Field("title") title: string, @Field("content") content: string): Promise<Response> { return <Response> {} }; | ||
@POST("/posts") | ||
async auth(@Body body: OAuth): Promise<Response<Token>> { return <Response<Token>>{} }; | ||
} | ||
``` | ||
### Field | ||
- Position: Method Parameter | ||
`Field` decorator parameterizes the field in HTTP request. It only takes effect when method is decorated by **@FormUrlEncoded**. | ||
```typescript | ||
@BasePath("") | ||
export class AuthService extends BaseService { | ||
@POST("/oauth/token") | ||
@FormUrlEncoded | ||
async createPost2(@FieldMap post: IPost): Promise<Response> { return <Response> {} }; | ||
async auth(@Field("username") username: string, @Field("password") password: string): Promise<Response<Token>> { return <Response<Token>>{} }; | ||
} | ||
``` | ||
@BasePath(API_PREFIX) | ||
### FieldMap | ||
- Position: Method Parameter | ||
`FieldMap` decorator parameterizes the field in HTTP request. It only takes effect when method is decorated by **@FormUrlEncoded**. | ||
```typescript | ||
@BasePath("") | ||
export class AuthService extends BaseService { | ||
@POST("/oauth/token") | ||
@FormUrlEncoded | ||
async auth(@FieldMap fields: OAuth): Promise<Response<Token>> { return <Response<Token>>{} }; | ||
} | ||
``` | ||
### Multipart | ||
- Position: Method | ||
`Multipart` decorator declares that the content type is **multipart/form-data** in HTTP request. | ||
```typescript | ||
@BasePath("/api/v1") | ||
export class FileService extends BaseService { | ||
@POST("/upload") | ||
@Multipart | ||
async upload(@Part("bucket") bucket: PartDescriptor<string>, @Part("file") file: PartDescriptor<Buffer>): Promise<Response> { return <Response> {} }; | ||
async upload(@Part("bucket") bucket: PartDescriptor<string>, @Part("file") file: PartDescriptor<Buffer>): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
### Part | ||
- Position: Method Parameter | ||
`Part` decorator parameterizes the part in HTTP request. It only takes effect when method is decorated by **@Multipart**. | ||
```typescript | ||
@BasePath("/api/v1") | ||
export class FileService extends BaseService { | ||
@POST("/upload") | ||
@Multipart | ||
async uploadMulti(@Part("bucket") bucket: PartDescriptor<string>, @Part("files") files: PartDescriptor<Buffer>[]): Promise<Response> { return <Response> {} }; | ||
async upload(@Part("bucket") bucket: PartDescriptor<string>, @Part("file") file: PartDescriptor<Buffer>): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
(async () => { | ||
const userService = new ServiceBuilder() | ||
.setEndpoint(TEST_SERVER_ENDPOINT) | ||
.build(UserService); | ||
const response = await userService.getUsers(TOKEN); | ||
// use response.data ... | ||
})() | ||
### ResponseType | ||
- Position: Method | ||
- Options: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' | ||
`ResponseType` decorator declares response type in axios config. | ||
```typescript | ||
@BasePath("/api/v1") | ||
export class FileService extends BaseService { | ||
@GET("/file") | ||
@ResponseType("stream") | ||
async getFile(@Path("fileId") fileId: string): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
See [test](test/ts-retrofit.test.ts) to get more examples. | ||
### RequestTransformer | ||
## Decorators | ||
- Position: Method | ||
| Category | Name | Description | Decorator Position | Example | | ||
| :--------------: | :-------------: | :----------------------------------------------------------: | :----------------: | :----------------------------------------------------------: | | ||
| HTTP Method | @GET | GET Method | Method | @GET("/users") | | ||
| HTTP Method | @POST | POST Method | Method | @POST("/users") | | ||
| HTTP Method | @PUT | PUT Method | Method | @PUT("/users/{userId}") | | ||
| HTTP Method | @PATCH | PATCH Method | Method | @PATCH("/users/{userId}") | | ||
| HTTP Method | @DELETE | DELETE Method | Method | @DELETE("/users/{userId}") | | ||
| HTTP Method | @HEAD | HEAD Method | Method | @HEAD("/users/{userId}") | | ||
| HTTP Method | @OPTIONS | OPTIONS Method | Method | @OPTIONS("/users/{userId}") | | ||
| Base Path | @BasePath | Specifying the base path of a series of API endpoints | Class | @BasePath("/api/v1") | | ||
| Static Headers | @Headers | Specifying the static headers of API endpoint | Method | @Headers({ "content-type": "application/x-www-form-urlencoded", "Accept": "application/json" }) | | ||
| Header Parameter | @Header | Parameterized header | Method Parameter | @Header("X-Token") | | ||
| Header Parameters | @HeaderMap | Parameterized header | Method Parameter | @HeaderMap | | ||
| Path Parameter | @Path | Specifying parameter in path of API | Method Parameter | @PathParam("userId") | | ||
| Body | @Body | Specifying body data | Method Parameter | @Body | | ||
| Static Query | @Queries | Specifying static query data | Method | @Queries({ page: 1, size: 20, sort: "createdAt:desc" }) | | ||
| Query Parameter | @Query | Parameterized query | Method Parameter | @Query("group") | | ||
| Query Parameters | @QueryMap | Parameterized query | Method Parameter | @QueryMap | | ||
| Static Headers | @FormUrlEncoded | Specifying "content-type" to be "application/x-www-form-urlencoded" | Method | @FormUrlEncoded | | ||
| Field Parameter | @Field | Specifying field in method parameter, only effective when method has been decorated by @FormUrlEncoded | Method Parameter | @Field("name") | | ||
| Field Parameters | @FieldMap | Specifying field map in method parameter, only effective when method has been decorated by @FormUrlEncoded | Method Parameter | @FieldMap | | ||
| Static Headers | @Multipart | Specifying "content-type" to be "multipart/form-data" | Method | @Multipart | | ||
| Part Parameters | @Part | Specifying field map in method parameter, only effective when method has been decorated by @Multipart | Method Parameter | @Part("name") | | ||
| Response | @ResponseType | Specifying the response type in axios config| Method | @ResponseType("stream") | | ||
`RequestTransformer` decorator provides a hook to handle request data before sending request to server. | ||
```typescript | ||
@BasePath(API_PREFIX) | ||
export class TransformerService extends BaseService { | ||
@POST("/request-transformer") | ||
@RequestTransformer((data: any, headers?: any) => { | ||
data.foo = 'foo'; // add something to request data | ||
return JSON.stringify(data); | ||
}) | ||
async createSomething(@Body body: Something): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
### ResponseTransformer | ||
- Position: Method | ||
`ResponseTransformer` decorator provides a hook to handle response data after receiving response from server. | ||
```typescript | ||
@BasePath(API_PREFIX) | ||
export class TransformerService extends BaseService { | ||
@POST("/request-transformer") | ||
@RequestTransformer((data: any, headers?: any) => { | ||
data.foo = 'foo'; // add something to response data | ||
return JSON.stringify(data); | ||
}) | ||
async createSomething(@Body body: Something): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
### Timeout | ||
- Position: Method | ||
`Timeout` decorator declares timeout in axios config. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
@GET("/items") | ||
@Timeout(3000) | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### ResponseStatus | ||
- Position: Method | ||
`ResponseStatus` decorator declares status code for method, do nothing just a declaration. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
@GET("/items") | ||
@Timeout(3000) | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### Config | ||
- Position: Method | ||
`Config` decorator provides a direct way to set config for a request in axios. | ||
```typescript | ||
@BasePath("/api/v1") | ||
export class ConfigService extends BaseService { | ||
@GET("/config") | ||
@Config({ | ||
maxRedirects: 1, | ||
}) | ||
async getConfig(): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
### Decorators Summary | ||
| Category | Name | Description | Decorator Position | Example | | ||
| :-----------------: | :------------------: | :--------------------------------------: | :----------------: | :--------------------------------------: | | ||
| HTTP Method | @GET | GET Method | Method | @GET("/users") | | ||
| HTTP Method | @POST | POST Method | Method | @POST("/users") | | ||
| HTTP Method | @PUT | PUT Method | Method | @PUT("/users/{userId}") | | ||
| HTTP Method | @PATCH | PATCH Method | Method | @PATCH("/users/{userId}") | | ||
| HTTP Method | @DELETE | DELETE Method | Method | @DELETE("/users/{userId}") | | ||
| HTTP Method | @HEAD | HEAD Method | Method | @HEAD("/users/{userId}") | | ||
| HTTP Method | @OPTIONS | OPTIONS Method | Method | @OPTIONS("/users/{userId}") | | ||
| Base Path | @BasePath | Specifying the base path of a series of API endpoints | Class | @BasePath("/api/v1") | | ||
| Static Headers | @Headers | Specifying the static headers of API endpoint | Method | @Headers({ "content-type": "application/x-www-form-urlencoded", "Accept": "application/json" }) | | ||
| Header Parameter | @Header | Parameterized header | Method Parameter | @Header("X-Token") | | ||
| Header Parameters | @HeaderMap | Parameterized header | Method Parameter | @HeaderMap | | ||
| Path Parameter | @Path | Specifying parameter in path of API | Method Parameter | @Path("userId") | | ||
| Body | @Body | Specifying body data | Method Parameter | @Body | | ||
| Static Query | @Queries | Specifying static query data | Method | @Queries({ page: 1, size: 20, sort: "createdAt:desc" }) | | ||
| Query Parameter | @Query | Parameterized query | Method Parameter | @Query("group") | | ||
| Query Parameters | @QueryMap | Parameterized query | Method Parameter | @QueryMap | | ||
| Static Headers | @FormUrlEncoded | Specifying "content-type" to be "application/x-www-form-urlencoded" | Method | @FormUrlEncoded | | ||
| Field Parameter | @Field | Specifying field in method parameter, only takes effect when method has been decorated by @FormUrlEncoded | Method Parameter | @Field("name") | | ||
| Field Parameters | @FieldMap | Specifying field map in method parameter, only takes effect when method has been decorated by @FormUrlEncoded | Method Parameter | @FieldMap | | ||
| Static Headers | @Multipart | Specifying "content-type" to be "multipart/form-data" | Method | @Multipart | | ||
| Part Parameters | @Part | Specifying field map in method parameter, only takes effect when method has been decorated by @Multipart | Method Parameter | @Part("name") | | ||
| Response | @ResponseType | Specifying the response type in axios config | Method | @ResponseType("stream") | | ||
| RequestTransformer | @RequestTransformer | Specifying the request transformer in axios config | Method | @RequestTransformer((data: any, headers?: any) => { data.foo = 'foo'; return JSON.stringify(data); }) | | ||
| ResponseTransformer | @ResponseTransformer | Specifying the response transformer in axios config | Method | @ResponseTransformer((data: any, headers?: any) => { const json = JSON.parse(data); json.foo = 'foo'; return json; }) | | ||
| Timeout | @Timeout | Specifying the timeout in axios config | Method | @Timeout(5000) | | ||
| ResponseStatus | @ResponseStatus | Declare response status code for method, do nothing just a declaration | Method | @ResponseStatus(204) | | ||
| Config | @Config | A direct way to set config for a request in axios | Method | @Config({ maxRedirects: 1 }) | | ||
## Test | ||
@@ -177,0 +582,0 @@ |
616
README.md
@@ -6,4 +6,4 @@ # ts-retrofit | ||
| Statements | Branches | Functions | Lines | | ||
| -----------|----------|-----------|-------| | ||
| Statements | Branches | Functions | Lines | | ||
| ---------------------------------------- | ---------------------------------------- | ---------------------------------------- | ---------------------------------------- | | ||
| ![Statements](https://img.shields.io/badge/Coverage-98.99%25-brightgreen.svg "Make me better!") | ![Branches](https://img.shields.io/badge/Coverage-83.87%25-yellow.svg "Make me better!") | ![Functions](https://img.shields.io/badge/Coverage-98.67%25-brightgreen.svg "Make me better!") | ![Lines](https://img.shields.io/badge/Coverage-98.99%25-brightgreen.svg "Make me better!") | | ||
@@ -21,161 +21,561 @@ | ||
Here is a typical service definition and usage: | ||
```typescript | ||
import { | ||
GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, BasePath, Header, Queries, Query, | ||
Headers, Path, QueryMap, Body, FormUrlEncoded, Field, FieldMap, | ||
BaseService, ServiceBuilder, Response | ||
} from "ts-retrofit"; | ||
export const TEST_SERVER_HOST = "http://localhost"; | ||
export const TEST_SERVER_PORT = 12345; | ||
export const TEST_SERVER_ENDPOINT = `${TEST_SERVER_HOST}:${TEST_SERVER_PORT}`; | ||
export const API_PREFIX = "/api/v1"; | ||
export const TOKEN = "abcdef123456"; | ||
import { GET, POST, PUT, PATCH, DELETE, BasePath, Header, Path, Body, BaseService, ServiceBuilder, Response } from "ts-retrofit"; | ||
export interface IUser { | ||
interface User { | ||
id?: number; | ||
name: string; | ||
age: number; | ||
[x: string]: any; | ||
email: string; | ||
} | ||
export interface ISearchQuery { | ||
title?: string; | ||
author?: string; | ||
category?: string; | ||
} | ||
export interface IAuth { | ||
username: string; | ||
password: string; | ||
} | ||
export interface IPost { | ||
title: string; | ||
content: string; | ||
} | ||
@BasePath(API_PREFIX) | ||
export class UserService extends BaseService { | ||
@BasePath("/api/v1") | ||
class UserService extends BaseService { | ||
@GET("/users") | ||
async getUsers(@Header("X-Token") token: string): Promise<Response> { return <Response> {} }; | ||
async getUsers(@Header("Authorization") authorization: string): Promise<Response<Array<User>>> { return <Response<Array<User>>> {} }; | ||
@GET("/users/{userId}") | ||
async getUser(@Header("X-Token") token: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
async getUser(@Header("Authorization") authorization: string, @Path("userId") userId: number): Promise<Response<User>> { return <Response<User>> {} }; | ||
@POST("/users") | ||
async createUser(@Header("X-Token") token: string, @Body user: IUser): Promise<Response> { return <Response> {} }; | ||
async createUser(@Header("Authorization") authorization: string, @Body user: User): Promise<Response> { return <Response> {} }; | ||
@PUT("/users/{userId}") | ||
async replaceUser(@Header("X-Token") token: string, @Path("userId") userId: number, @Body user: IUser): Promise<Response> { return <Response> {} }; | ||
async updateUser(@Header("Authorization") authorization: string, @Path("userId") userId: number, @Body user: User): Promise<Response> { return <Response> {} }; | ||
@PATCH("/users/{userId}") | ||
async updateUser(@Header("X-Token") token: string, @Path("userId") userId: number, @Body user: Partial<IUser>): Promise<Response> { return <Response> {} }; | ||
async patchUser(@Header("Authorization") authorization: string, @Path("userId") userId: number, @Body user: Partial<User>): Promise<Response> { return <Response> {} }; | ||
@DELETE("/users/{userId}") | ||
async deleteUser(@Header("X-Token") token: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
async deleteUser(@Header("Authorization") authorization: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
} | ||
@HEAD("/users/{userId}") | ||
async headUser(@Header("X-Token") token: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
(async () => { | ||
const authorization = "foobar"; | ||
const userService = new ServiceBuilder() | ||
.setEndpoint("https://www.your-host.com") | ||
.build(UserService); | ||
const response = await userService.getUsers(authorization); | ||
// Now you can get response data from response.data | ||
})() | ||
``` | ||
@OPTIONS("/users/{userId}") | ||
async optionsUser(@Header("X-Token") token: string, @Path("userId") userId: number): Promise<Response> { return <Response> {} }; | ||
You can see [test file](test/ts-retrofit.test.ts) to get more examples. | ||
## ServiceBuilder | ||
Example: | ||
```typescript | ||
import { AxiosRequestConfig } from "axios"; | ||
import { | ||
ServiceBuilder, | ||
ResponseInterceptorFunction, | ||
RequestInterceptorFunction, | ||
RequestInterceptor, | ||
ResponseInterceptor, | ||
} from "ts-retrofit"; | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
@GET("/items") | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
@BasePath(API_PREFIX) | ||
export class SearchService extends BaseService { | ||
@GET("/search") | ||
async search(@Header("X-Token") token: string, @QueryMap query: ISearchQuery): Promise<Response> { return <Response> {} }; | ||
const RequestInterceptor: RequestInterceptorFunction = (config) => { | ||
console.log("Before sending request to server."); | ||
return config; | ||
}; | ||
const ResponseInterceptor: ResponseInterceptorFunction = (response) => { | ||
console.log("After receiving response from server."); | ||
return response; | ||
}; | ||
(async () => { | ||
const itemService = new ServiceBuilder() | ||
.setEndpoint(${ENDPOINT}) | ||
.setStandalone(true) | ||
.setRequestInterceptors(RequestInterceptor) | ||
.setResponseInterceptors(ResponseInterceptor) | ||
.build(ItemService); | ||
const response: any = await itemService.getVersion(); | ||
console.log(response.data); | ||
})(); | ||
// outputs: | ||
// Before sending request to server. | ||
// After receiving response from server. | ||
// <response data> | ||
``` | ||
## Decorators | ||
### BasePath | ||
* Position: Class | ||
`BasePath` decorator declares the path prefix of a service. | ||
```typescript | ||
// The common path of ItemService is ${ENDPOINT}/api/v1 | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService {} | ||
``` | ||
### HTTP Method Decorators | ||
All HTTP method decorators have an optional second parameter with type **HttpMethodOptions**: | ||
```typescript | ||
export interface HttpMethodOptions { | ||
ignoreBasePath?: boolean; | ||
} | ||
``` | ||
#### GET | ||
* Position: Method | ||
`GET` decorator declares that what it decorated use HTTP **GET** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
@GET("/items") | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
// GET ${ENDPOINT}/items | ||
@GET("/items", { ignoreBasePath: true }) | ||
async getItemsWithoutBasePath(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
#### POST | ||
* Position: Method | ||
`POST` decorator declares that what it decorated use HTTP **POST** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// POST ${ENDPOINT}/api/v1/items | ||
@POST("/items") | ||
async createItem(@Body item: Item): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### PUT | ||
* Position: Method | ||
`PUT` decorator declares that what it decorated use HTTP **PUT** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// PUT ${ENDPOINT}/api/v1/items/{itemId} | ||
@PUT("/items/{itemId}") | ||
async updateItem(@Path("itemId") itemId: number, @Body item: Item): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### PATCH | ||
* Position: Method | ||
`PATCH` decorator declares that what it decorated use HTTP **PATCH** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// PATCH ${ENDPOINT}/api/v1/items/{itemId} | ||
@PATCH("/items/{itemId}") | ||
async patchItem(@Path("itemId") itemId: number, @Body item: Partial<Item>): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### DELETE | ||
* Position: Method | ||
`DELETE` decorator declares that what it decorated use HTTP **DELETE** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// DELETE ${ENDPOINT}/api/v1/items/{itemId} | ||
@DELETE("/items/{itemId}") | ||
async deleteItem(@Path("itemId") itemId: number): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### HEAD | ||
* Position: Method | ||
`HEAD` decorator declares that what it decorated use HTTP **HEAD** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class FileService extends BaseService { | ||
// HEAD ${ENDPOINT}/api/v1/files/{fileId} | ||
@HEAD("/files/{fileId}") | ||
async getFileMetaInfo(@Path("fileId") fileId: number): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
#### OPTIONS | ||
* Position: Method | ||
`OPTIONS` decorator declares that what it decorated use HTTP **OPTIONS** method to request server. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// OPTIONS ${ENDPOINT}/api/v1/items/{itemId} | ||
@OPTIONS("/items/{itemId}") | ||
async getFileMetaInfo(@Path("itemId") itemId: number): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
### Headers | ||
* Position: Method | ||
`Headers` decorator declares that what **static HTTP headers** should be added to request. | ||
```typescript | ||
@BasePath("") | ||
export class AuthService extends BaseService { | ||
@POST("/oauth2/authorize") | ||
@POST("/oauth/token") | ||
@Headers({ | ||
"content-type": "application/x-www-form-urlencoded", | ||
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8", | ||
"Accept": "application/json" | ||
}) | ||
async auth(@Body body: IAuth): Promise<Response> { return <Response> {} }; | ||
async auth(@Body body: OAuth): Promise<Response<Token>> { return <Response<Token>>{} }; | ||
} | ||
``` | ||
@BasePath(API_PREFIX) | ||
export class PostService extends BaseService { | ||
@GET("/posts") | ||
### Header | ||
* Position: Method Parameter | ||
`Header` decorator parameterizes the header in HTTP request. Client can provide a value to **one header**. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
async getItems(@Header("Authorization") authorization: string): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### HeaderMap | ||
- Position: Method Parameter | ||
`HeaderMap` decorator parameterizes the headers in HTTP request. Client can provide values to **multi headers**. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
async getItems(@HeaderMap headers: any): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### Path | ||
- Position: Method Parameter | ||
`Path` decorator parameterizes the part of path in HTTP request. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items/{itemId} | ||
@GET("/items/{itemId}") | ||
async getItem(@Path("itemId") itemId: number): Promise<Response<Item>> { return <Response<Item>> {} }; | ||
} | ||
``` | ||
### Body | ||
- Position: Method Parameter | ||
`Body` decorator parameterizes the body in HTTP request. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// POST ${ENDPOINT}/api/v1/items | ||
@POST("/items") | ||
async createItem(@Body item: Item): Promise<Response> { return <Response> {} }; | ||
} | ||
``` | ||
### Queries | ||
- Position: Method | ||
`Queries` decorator declares that what **static queries** should be added to request. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items?size=20 | ||
@GET("/items") | ||
@Queries({ | ||
page: 1, | ||
size: 20, | ||
sort: "createdAt:desc", | ||
}) | ||
async getPosts(): Promise<Response> { return <Response> {} }; | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
@GET("/posts") | ||
### Query | ||
- Position: Method Parameter | ||
`Query` decorator parameterizes the query in HTTP request. Client can provide a value to **one query parameter**. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items?size=20 | ||
@GET("/items") | ||
@Queries({ | ||
page: 1, | ||
size: 20, | ||
sort: "createdAt:desc", | ||
}) | ||
async getPosts1(@Query('group') group: string): Promise<Response> { return <Response> {} }; | ||
@POST("/posts") | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### QueryMap | ||
- Position: Method Parameter | ||
`QueryMap` decorator parameterizes the queries in HTTP request. Client can provide values to **multi queries**. | ||
```typescript | ||
@BasePath("") | ||
class SearchService extends BaseService { | ||
// GET ${ENDPOINT}/search?a=foo&b=bar | ||
@GET("/search") | ||
async search(@QueryMap query: SearchQuery): Promise<Response<SearchResult>> { return <Response<SearchResult>> {} }; | ||
} | ||
``` | ||
### FormUrlEncoded | ||
- Position: Method | ||
`FormUrlEncoded` declares that the content type is **application/x-www-form-urlencoded;charset=utf-8** in HTTP request. | ||
```typescript | ||
@BasePath("") | ||
export class AuthService extends BaseService { | ||
@POST("/oauth/token") | ||
@FormUrlEncoded | ||
async createPost(@Field("title") title: string, @Field("content") content: string): Promise<Response> { return <Response> {} }; | ||
@POST("/posts") | ||
async auth(@Body body: OAuth): Promise<Response<Token>> { return <Response<Token>>{} }; | ||
} | ||
``` | ||
### Field | ||
- Position: Method Parameter | ||
`Field` decorator parameterizes the field in HTTP request. It only takes effect when method is decorated by **@FormUrlEncoded**. | ||
```typescript | ||
@BasePath("") | ||
export class AuthService extends BaseService { | ||
@POST("/oauth/token") | ||
@FormUrlEncoded | ||
async createPost2(@FieldMap post: IPost): Promise<Response> { return <Response> {} }; | ||
async auth(@Field("username") username: string, @Field("password") password: string): Promise<Response<Token>> { return <Response<Token>>{} }; | ||
} | ||
``` | ||
@BasePath(API_PREFIX) | ||
### FieldMap | ||
- Position: Method Parameter | ||
`FieldMap` decorator parameterizes the field in HTTP request. It only takes effect when method is decorated by **@FormUrlEncoded**. | ||
```typescript | ||
@BasePath("") | ||
export class AuthService extends BaseService { | ||
@POST("/oauth/token") | ||
@FormUrlEncoded | ||
async auth(@FieldMap fields: OAuth): Promise<Response<Token>> { return <Response<Token>>{} }; | ||
} | ||
``` | ||
### Multipart | ||
- Position: Method | ||
`Multipart` decorator declares that the content type is **multipart/form-data** in HTTP request. | ||
```typescript | ||
@BasePath("/api/v1") | ||
export class FileService extends BaseService { | ||
@POST("/upload") | ||
@Multipart | ||
async upload(@Part("bucket") bucket: PartDescriptor<string>, @Part("file") file: PartDescriptor<Buffer>): Promise<Response> { return <Response> {} }; | ||
async upload(@Part("bucket") bucket: PartDescriptor<string>, @Part("file") file: PartDescriptor<Buffer>): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
### Part | ||
- Position: Method Parameter | ||
`Part` decorator parameterizes the part in HTTP request. It only takes effect when method is decorated by **@Multipart**. | ||
```typescript | ||
@BasePath("/api/v1") | ||
export class FileService extends BaseService { | ||
@POST("/upload") | ||
@Multipart | ||
async uploadMulti(@Part("bucket") bucket: PartDescriptor<string>, @Part("files") files: PartDescriptor<Buffer>[]): Promise<Response> { return <Response> {} }; | ||
async upload(@Part("bucket") bucket: PartDescriptor<string>, @Part("file") file: PartDescriptor<Buffer>): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
(async () => { | ||
const userService = new ServiceBuilder() | ||
.setEndpoint(TEST_SERVER_ENDPOINT) | ||
.build(UserService); | ||
const response = await userService.getUsers(TOKEN); | ||
// use response.data ... | ||
})() | ||
### ResponseType | ||
- Position: Method | ||
- Options: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' | ||
`ResponseType` decorator declares response type in axios config. | ||
```typescript | ||
@BasePath("/api/v1") | ||
export class FileService extends BaseService { | ||
@GET("/file") | ||
@ResponseType("stream") | ||
async getFile(@Path("fileId") fileId: string): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
See [test](test/ts-retrofit.test.ts) to get more examples. | ||
### RequestTransformer | ||
## Decorators | ||
- Position: Method | ||
| Category | Name | Description | Decorator Position | Example | | ||
| :--------------: | :-------------: | :----------------------------------------------------------: | :----------------: | :----------------------------------------------------------: | | ||
| HTTP Method | @GET | GET Method | Method | @GET("/users") | | ||
| HTTP Method | @POST | POST Method | Method | @POST("/users") | | ||
| HTTP Method | @PUT | PUT Method | Method | @PUT("/users/{userId}") | | ||
| HTTP Method | @PATCH | PATCH Method | Method | @PATCH("/users/{userId}") | | ||
| HTTP Method | @DELETE | DELETE Method | Method | @DELETE("/users/{userId}") | | ||
| HTTP Method | @HEAD | HEAD Method | Method | @HEAD("/users/{userId}") | | ||
| HTTP Method | @OPTIONS | OPTIONS Method | Method | @OPTIONS("/users/{userId}") | | ||
| Base Path | @BasePath | Specifying the base path of a series of API endpoints | Class | @BasePath("/api/v1") | | ||
| Static Headers | @Headers | Specifying the static headers of API endpoint | Method | @Headers({ "content-type": "application/x-www-form-urlencoded", "Accept": "application/json" }) | | ||
| Header Parameter | @Header | Parameterized header | Method Parameter | @Header("X-Token") | | ||
| Header Parameters | @HeaderMap | Parameterized header | Method Parameter | @HeaderMap | | ||
| Path Parameter | @Path | Specifying parameter in path of API | Method Parameter | @PathParam("userId") | | ||
| Body | @Body | Specifying body data | Method Parameter | @Body | | ||
| Static Query | @Queries | Specifying static query data | Method | @Queries({ page: 1, size: 20, sort: "createdAt:desc" }) | | ||
| Query Parameter | @Query | Parameterized query | Method Parameter | @Query("group") | | ||
| Query Parameters | @QueryMap | Parameterized query | Method Parameter | @QueryMap | | ||
| Static Headers | @FormUrlEncoded | Specifying "content-type" to be "application/x-www-form-urlencoded" | Method | @FormUrlEncoded | | ||
| Field Parameter | @Field | Specifying field in method parameter, only effective when method has been decorated by @FormUrlEncoded | Method Parameter | @Field("name") | | ||
| Field Parameters | @FieldMap | Specifying field map in method parameter, only effective when method has been decorated by @FormUrlEncoded | Method Parameter | @FieldMap | | ||
| Static Headers | @Multipart | Specifying "content-type" to be "multipart/form-data" | Method | @Multipart | | ||
| Part Parameters | @Part | Specifying field map in method parameter, only effective when method has been decorated by @Multipart | Method Parameter | @Part("name") | | ||
| Response | @ResponseType | Specifying the response type in axios config| Method | @ResponseType("stream") | | ||
| RequestTransformer | @RequestTransformer | Specifying the request transformer in axios config| Method | @RequestTransformer((data: any, headers?: any) => { data.foo = 'foo'; return JSON.stringify(data); }) | | ||
| ResponseTransformer | @ResponseTransformer | Specifying the response transformer in axios config| Method | @ResponseTransformer((data: any, headers?: any) => { const json = JSON.parse(data); json.foo = 'foo'; return json; }) | | ||
| Timeout | @Timeout | Specifying the timeout in axios config| Method | @Timeout(5000) | | ||
| ResponseStatus | @ResponseStatus | Declare response status code for method, do nothing just a declaration | Method | @ResponseStatus(204) | | ||
| Config | @Config | A direct way to set config for a request in axios | Method | @Config({ maxRedirects: 1 }) | | ||
`RequestTransformer` decorator provides a hook to handle request data before sending request to server. | ||
```typescript | ||
@BasePath(API_PREFIX) | ||
export class TransformerService extends BaseService { | ||
@POST("/request-transformer") | ||
@RequestTransformer((data: any, headers?: any) => { | ||
data.foo = 'foo'; // add something to request data | ||
return JSON.stringify(data); | ||
}) | ||
async createSomething(@Body body: Something): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
### ResponseTransformer | ||
- Position: Method | ||
`ResponseTransformer` decorator provides a hook to handle response data after receiving response from server. | ||
```typescript | ||
@BasePath(API_PREFIX) | ||
export class TransformerService extends BaseService { | ||
@POST("/request-transformer") | ||
@RequestTransformer((data: any, headers?: any) => { | ||
data.foo = 'foo'; // add something to response data | ||
return JSON.stringify(data); | ||
}) | ||
async createSomething(@Body body: Something): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
### Timeout | ||
- Position: Method | ||
`Timeout` decorator declares timeout in axios config. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
@GET("/items") | ||
@Timeout(3000) | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### ResponseStatus | ||
- Position: Method | ||
`ResponseStatus` decorator declares status code for method, do nothing just a declaration. | ||
```typescript | ||
@BasePath("/api/v1") | ||
class ItemService extends BaseService { | ||
// GET ${ENDPOINT}/api/v1/items | ||
@GET("/items") | ||
@Timeout(3000) | ||
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} }; | ||
} | ||
``` | ||
### Config | ||
- Position: Method | ||
`Config` decorator provides a direct way to set config for a request in axios. | ||
```typescript | ||
@BasePath("/api/v1") | ||
export class ConfigService extends BaseService { | ||
@GET("/config") | ||
@Config({ | ||
maxRedirects: 1, | ||
}) | ||
async getConfig(): Promise<Response> { return <Response>{} }; | ||
} | ||
``` | ||
### Decorators Summary | ||
| Category | Name | Description | Decorator Position | Example | | ||
| :-----------------: | :------------------: | :--------------------------------------: | :----------------: | :--------------------------------------: | | ||
| HTTP Method | @GET | GET Method | Method | @GET("/users") | | ||
| HTTP Method | @POST | POST Method | Method | @POST("/users") | | ||
| HTTP Method | @PUT | PUT Method | Method | @PUT("/users/{userId}") | | ||
| HTTP Method | @PATCH | PATCH Method | Method | @PATCH("/users/{userId}") | | ||
| HTTP Method | @DELETE | DELETE Method | Method | @DELETE("/users/{userId}") | | ||
| HTTP Method | @HEAD | HEAD Method | Method | @HEAD("/users/{userId}") | | ||
| HTTP Method | @OPTIONS | OPTIONS Method | Method | @OPTIONS("/users/{userId}") | | ||
| Base Path | @BasePath | Specifying the base path of a series of API endpoints | Class | @BasePath("/api/v1") | | ||
| Static Headers | @Headers | Specifying the static headers of API endpoint | Method | @Headers({ "content-type": "application/x-www-form-urlencoded", "Accept": "application/json" }) | | ||
| Header Parameter | @Header | Parameterized header | Method Parameter | @Header("X-Token") | | ||
| Header Parameters | @HeaderMap | Parameterized header | Method Parameter | @HeaderMap | | ||
| Path Parameter | @Path | Specifying parameter in path of API | Method Parameter | @Path("userId") | | ||
| Body | @Body | Specifying body data | Method Parameter | @Body | | ||
| Static Query | @Queries | Specifying static query data | Method | @Queries({ page: 1, size: 20, sort: "createdAt:desc" }) | | ||
| Query Parameter | @Query | Parameterized query | Method Parameter | @Query("group") | | ||
| Query Parameters | @QueryMap | Parameterized query | Method Parameter | @QueryMap | | ||
| Static Headers | @FormUrlEncoded | Specifying "content-type" to be "application/x-www-form-urlencoded" | Method | @FormUrlEncoded | | ||
| Field Parameter | @Field | Specifying field in method parameter, only takes effect when method has been decorated by @FormUrlEncoded | Method Parameter | @Field("name") | | ||
| Field Parameters | @FieldMap | Specifying field map in method parameter, only takes effect when method has been decorated by @FormUrlEncoded | Method Parameter | @FieldMap | | ||
| Static Headers | @Multipart | Specifying "content-type" to be "multipart/form-data" | Method | @Multipart | | ||
| Part Parameters | @Part | Specifying field map in method parameter, only takes effect when method has been decorated by @Multipart | Method Parameter | @Part("name") | | ||
| Response | @ResponseType | Specifying the response type in axios config | Method | @ResponseType("stream") | | ||
| RequestTransformer | @RequestTransformer | Specifying the request transformer in axios config | Method | @RequestTransformer((data: any, headers?: any) => { data.foo = 'foo'; return JSON.stringify(data); }) | | ||
| ResponseTransformer | @ResponseTransformer | Specifying the response transformer in axios config | Method | @ResponseTransformer((data: any, headers?: any) => { const json = JSON.parse(data); json.foo = 'foo'; return json; }) | | ||
| Timeout | @Timeout | Specifying the timeout in axios config | Method | @Timeout(5000) | | ||
| ResponseStatus | @ResponseStatus | Declare response status code for method, do nothing just a declaration | Method | @ResponseStatus(204) | | ||
| Config | @Config | A direct way to set config for a request in axios | Method | @Config({ maxRedirects: 1 }) | | ||
## Test | ||
@@ -182,0 +582,0 @@ |
86227
16
1304
584