Security News
JSR Working Group Kicks Off with Ambitious Roadmap and Plans for Open Governance
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
ts-retrofit
Advanced tools
A declarative and axios based retrofit implementation for JavaScript and TypeScript.
Statements | Branches | Functions | Lines |
---|---|---|---|
A declarative and axios based retrofit implementation for JavaScript and TypeScript.
$ npm i ts-retrofit
Here is a typical service definition and usage:
import { GET, POST, PUT, PATCH, DELETE, BasePath, Header, Path, Body, BaseService, ServiceBuilder, Response } from "ts-retrofit";
interface User {
id?: number;
name: string;
email: string;
}
@BasePath("/api/v1")
class UserService extends BaseService {
@GET("/users")
async getUsers(@Header("Authorization") authorization: string): Promise<Response<Array<User>>> { return <Response<Array<User>>> {} };
@GET("/users/{userId}")
async getUser(@Header("Authorization") authorization: string, @Path("userId") userId: number): Promise<Response<User>> { return <Response<User>> {} };
@POST("/users")
async createUser(@Header("Authorization") authorization: string, @Body user: User): Promise<Response> { return <Response> {} };
@PUT("/users/{userId}")
async updateUser(@Header("Authorization") authorization: string, @Path("userId") userId: number, @Body user: User): Promise<Response> { return <Response> {} };
@PATCH("/users/{userId}")
async patchUser(@Header("Authorization") authorization: string, @Path("userId") userId: number, @Body user: Partial<User>): Promise<Response> { return <Response> {} };
@DELETE("/users/{userId}")
async deleteUser(@Header("Authorization") authorization: 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
})()
You can see test file to get more examples.
Example:
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>>> {} };
}
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>
You can set log callback to print some information after request finished (ok/error):
@BasePath("")
export class HealthService extends BaseService {
@GET("/ping")
@ResponseStatus(200)
async ping(): Promise<Response> { return <Response>{} };
}
const myLogCallback = (config: RequestConfig, response: Response) => {
const log = `[${config.method}] ${config.url} ${response.status}`;
console.log(log); // [GET] http://localhost:12345/ping 200
};
const service = new ServiceBuilder()
.setEndpoint("http://localhost:12345")
.setLogCallback(myLogCallback)
.build(HealthService);
// or use this
service.setLogCallback(myLogCallback);
const response = await service.ping();
BasePath
decorator declares the path prefix of a service.
// The common path of ItemService is ${ENDPOINT}/api/v1
@BasePath("/api/v1")
class ItemService extends BaseService {}
All HTTP method decorators have an optional second parameter with type HttpMethodOptions:
export interface HttpMethodOptions {
ignoreBasePath?: boolean;
}
GET
decorator declares that what it decorated use HTTP GET method to request server.
@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
decorator declares that what it decorated use HTTP POST method to request server.
@BasePath("/api/v1")
class ItemService extends BaseService {
// POST ${ENDPOINT}/api/v1/items
@POST("/items")
async createItem(@Body item: Item): Promise<Response> { return <Response> {} };
}
PUT
decorator declares that what it decorated use HTTP PUT method to request server.
@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
decorator declares that what it decorated use HTTP PATCH method to request server.
@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
decorator declares that what it decorated use HTTP DELETE method to request server.
@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
decorator declares that what it decorated use HTTP HEAD method to request server.
@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
decorator declares that what it decorated use HTTP OPTIONS method to request server.
@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
decorator declares that what static HTTP headers should be added to request.
@BasePath("")
export class AuthService extends BaseService {
@POST("/oauth/token")
@Headers({
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"Accept": "application/json"
})
async auth(@Body body: OAuth): Promise<Response<Token>> { return <Response<Token>>{} };
}
Header
decorator parameterizes the header in HTTP request. Client can provide a value to one header.
@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
decorator parameterizes the headers in HTTP request. Client can provide values to multi headers.
@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
decorator parameterizes the part of path in HTTP request.
@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
decorator parameterizes the body in HTTP request.
@BasePath("/api/v1")
class ItemService extends BaseService {
// POST ${ENDPOINT}/api/v1/items
@POST("/items")
async createItem(@Body item: Item): Promise<Response> { return <Response> {} };
}
Queries
decorator declares that what static queries should be added to request.
@BasePath("/api/v1")
class ItemService extends BaseService {
// GET ${ENDPOINT}/api/v1/items?size=20
@GET("/items")
@Queries({
size: 20,
})
async getItems(): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} };
}
Query
decorator parameterizes the query in HTTP request. Client can provide a value to one query parameter.
@BasePath("/api/v1")
class ItemService extends BaseService {
// GET ${ENDPOINT}/api/v1/items?size=20
@GET("/items")
async getItems(@Query('size') size: number): Promise<Response<Array<Item>>> { return <Response<Array<Item>>> {} };
}
QueryMap
decorator parameterizes the queries in HTTP request. Client can provide values to multi queries.
@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
declares that the content type is application/x-www-form-urlencoded;charset=utf-8 in HTTP request.
@BasePath("")
export class AuthService extends BaseService {
@POST("/oauth/token")
@FormUrlEncoded
async auth(@Body body: OAuth): Promise<Response<Token>> { return <Response<Token>>{} };
}
Field
decorator parameterizes the field in HTTP request. It only takes effect when method is decorated by @FormUrlEncoded.
@BasePath("")
export class AuthService extends BaseService {
@POST("/oauth/token")
@FormUrlEncoded
async auth(@Field("username") username: string, @Field("password") password: string): Promise<Response<Token>> { return <Response<Token>>{} };
}
FieldMap
decorator parameterizes the field in HTTP request. It only takes effect when method is decorated by @FormUrlEncoded.
@BasePath("")
export class AuthService extends BaseService {
@POST("/oauth/token")
@FormUrlEncoded
async auth(@FieldMap fields: OAuth): Promise<Response<Token>> { return <Response<Token>>{} };
}
Multipart
decorator declares that the content type is multipart/form-data in HTTP request.
@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>{} };
}
Part
decorator parameterizes the part in HTTP request. It only takes effect when method is decorated by @Multipart.
@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>{} };
}
ResponseType
decorator declares response type in axios config.
@BasePath("/api/v1")
export class FileService extends BaseService {
@GET("/file")
@ResponseType("stream")
async getFile(@Path("fileId") fileId: string): Promise<Response> { return <Response>{} };
}
RequestTransformer
decorator provides a hook to handle request data before sending request to server.
@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
decorator provides a hook to handle response data after receiving response from server.
@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
decorator declares timeout in axios config.
@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
decorator declares status code for method, do nothing just a declaration.
@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
decorator provides a direct way to set config for a request in axios.
@BasePath("/api/v1")
export class ConfigService extends BaseService {
@GET("/config")
@Config({
maxRedirects: 1,
})
async getConfig(): Promise<Response> { return <Response>{} };
}
GraphQL
decorator declares query for a GraphQL request.
GraphQLVariables
decorator declares variables for a GraphQL request.
const gqlQuery =
`query ($name: String!, $owner: String!) {
viewer {
name
location
}
repository(name: $name, owner: $owner) {
stargazerCount
forkCount
}
}`;
@BasePath("")
export class GraphQLService extends BaseService {
@POST("/graphql")
@GraphQL(gqlQuery, "UserAndRepo")
async graphql1(
@GraphQLVariables variables: any,
): Promise<Response> { return <Response>{} };
}
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 }) |
GraphQL | @GraphQL | Declares query for a GraphQL request. | Method | @GraphQL(gqlQuery, "operationName") |
GraphQLVariables | @GraphQLVariables | Decorator declares variables for a GraphQL request. | Method | @GraphQLVariables |
$ npm test
FAQs
A declarative and axios based retrofit implementation for JavaScript and TypeScript.
The npm package ts-retrofit receives a total of 276 weekly downloads. As such, ts-retrofit popularity was classified as not popular.
We found that ts-retrofit demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
Security News
Research
An advanced npm supply chain attack is leveraging Ethereum smart contracts for decentralized, persistent malware control, evading traditional defenses.
Security News
Research
Attackers are impersonating Sindre Sorhus on npm with a fake 'chalk-node' package containing a malicious backdoor to compromise developers' projects.