Socket
Socket
Sign inDemoInstall

ts-retrofit

Package Overview
Dependencies
25
Maintainers
1
Versions
48
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.11.0 to 1.11.1

lib/types/util.d.ts

3

lib/baseService.js

@@ -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 @@

@@ -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 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc