@tushinski/ts-rest-node
v0.2.9
Published
Universal REST API client for TypeScript.
Downloads
1
Maintainers
Readme
ts-rest
Universal REST API client for TypeScript.
✅ Pure REST ✅ Strongly typed ✅ Customizable ✅ Tested ✅ JSON (de)serialization out of the box
Installation
browser version:
npm i @tushinski/ts-rest
node.js version:
npm i @tushinski/ts-rest-node
Quick start
☝ Create a client
import { getMapping, initClient } from 'ts-rest';
const restClient = {
users: {
get: getMapping<{}, { name: string }>(), // mapping for [GET] /users/:id
}
}
✌ Initialize
initClient({
client: restClient,
url: `https://example.com/rest`
})
🤟 Use
restClient.users.get('alex')
.then(user => console.log(user.name));
// the resulting request: [GET] https://example.com/rest/alex
Documentation
Client
A plain object representing the tree structure of a target API.
For example, for an API, providing these methods:
[GET] /docs/:id
[POST] /docs/
[PUT] /docs/:id
a client will look like this:
const client = {
docs: {
get: getMapping<{}, Document>(),
post: postMapping<Document, Document>(),
put: putMapping<Document, Document>()
}
}
(where Document
is a user-defined type)
Mappings
Ts-rest provides several functions for mappings. Their generic types are used to specify types of different request parameters.
GET (single resource)
Mapping:
{
get: getMapping<ParamsType, ResponseType>()
}
Usage:
client.get(id?: string, params?: ParamsType)
GET (collection)
Mapping:
{
getAll: getAllMapping<ParamsType, ResponseType>()
}
Usage:
client.getAll(params?: ParamsType)
POST
Mapping:
{
post: postMapping<DataType, ResponseType>()
}
Usage:
client.post(body: DataType)
PUT
Mapping:
{
put: putMapping<DataType, ResponseType>()
}
Usage (with resource id):
client.put(id: string, body: DataType) // [PUT] <api_url>/:id
Usage (on a current resource):
client.resource.put(body: DataType) // [PUT] <api_url>/resource
DELETE
Mapping:
{
delete: deleteMapping<DataType>()
}
Usage:
client.delete(id: string)
Mapping types
ResponseType
- type of response bodyDataType
- type of request bodyParamsType
- type of a search query parameters map
Search query parameters (ParamsType)
You can specify search query parameters using a plain object:
const moviesApiClient = {
movies: {
getAll: getAll<{genre: string, year: number}>()
}
}
// initialization...
moviesApiClient.movies.getAll({genre: 'drama', year: 1966})
Sub-resources
In most of the cases paths of single resources end with a single path parameter - a resource id. But there are cases when a resource contains nested collections (or sub-resources), like:
<api_path>/actors/{actorId}/movies
Ts-rest provides special function sub
for describing sub resources:
const client = {
actors: {
single: sub(() => ({
movies: {
getAll: getAllMapping<{}, Movie[]>()
},
}))
}
}
client.actors.single(1).movies.getAll() // [GET] <api_path>/actors/1/movies
.then(movies => {/*...*/})
You can think of it like of getting a single resource by id:
<api_path>/actors/1
and working with it's sub-resources:
<api_path>/actors/1/movies
Since the single
method only returns a "sub-client" (and doesn't perform any requests),
it's result can be stored to a variable for reusing:
const actor1 = client.actors.single(1);
actor1.movies.getAll()
.then(movies => /*...*/);
actor1.awards.post(/* award data */)
.then(award => /*...*/);
Sub-paths
For cases in which it is needed to declare a mapping for a varying sub-path,
there's a subPath
function:
const client = {
pdfDocument: subPath(/\.pdf$/, () => ({
get: getMapping<{}, Document>()
}))
}
client.pdfDocument("path/to/document/name.pdf").get() // [GET] <api_path>/path/to/document/name.pdf
.then(document => {/*...*/})
The first argument of subPath
is a regular expression which you can use to restrict sub-path.
If provided path doesn't match to the pattern, method will throw an error.
Request modifiers
Request modifiers are used to modify request parameters and response data during a request.
They can be specified with the requestModifiers
option (see Initialization).
Options modifier
Modifies default request parameters (such as headers, content type, etc.);
optionsModifier: (defaultOptions: RequestModification, path: string, method: HTTPMethod) => RequestModification
Body modifier
Modifies request body.
bodyModifier: (resp: Response, path: string, method: HTTPMethod) => any
Response modifier
Modifies response data.
responseModifier: (body: any, path: string, method: HTTPMethod) => BodyInit | null
Default modifiers
If custom modifiers are not specified, default modifiers are used:
{
optionsModifier: (defaultOptions) => defaultOptions,
bodyModifier: (body) => JSON.stringify(body),
responseModifier: (resp) => resp.json()
};