Package Exports
- typed-rest-api-client
Readme
typed-rest-api-client [beta-release]
Flexible, Type-Safe REST Client Generator for JavaScript & TypeScript
typed-rest-api-client is a lightweight utility for defining and consuming RESTful APIs using declarative, namespaced syntax. It supports interceptors, argument substitution, and works in both Node.js and browser environments.
✨ Features
- 🧠 Type-safe endpoint definitions with full TypeScript support
- 🧩 Composable API structure with namespace support
- 🔄 Request/response interceptors for parsing, logging, authentication, etc.
- 🌐 Compatible with browser and Node.js environments
- 🔄 Supports both CommonJS and ESM module formats
- 🤝 Works with popular libraries like axios
- 📦 Small bundle size for efficient builds
📦 Installation
npm install typed-rest-api-client🚀 Getting Started
import { rest } from 'typed-rest-api-client';
const api = rest({
baseUrl: 'https://api.no-domain/'
});
const users = api('users', ({ get, post, namespace }) => [
get('about'),
get('all', {
path: '/',
args: { limit: 100 } as {
limit?: number;
skip?: number;
take?: number;
},
schema: null! as { name: string }[],
config: { errorCodes: 500 } // normally >=400 will throw Error
}),
get('findOne', {
path: '/:id'
}),
post('create', {
path: '/:id'
}),
namespace('students', ({ get, post, put, del }) => [
post('create', { path: '/' }),
get('get', { path: '/:id', args: {} as { id: number } }),
put('update', { path: '/:id' }),
del('delete', { path: '/:id', args: {} as { id: number } })
])
]);
// calls: https://api.no-domain/users?skip=0&limit=100
// and `res` is object of type `{ name: string }[]`
const res = await users.all({ skip: 0 });
users.about();
users.findOne({ id: 42 });
users.students.get({ id: 5 });
users.students.update({ id: 5, name: 'Test' });
users.students.delete({ id: 5 });⚙️ Http client
A simple build in http client with interceptors functionality.
import { http } from 'typed-rest-api-client';
const client = http();
// The same as:
const client = http((reqInfo, init) => fetch(reqInfo, init));Built-in interceptors:
import { http, httpBodySerialize, httpErrorCode, httpJsonParser } from 'typed-rest-api-client';
const client = http.default();
// The same as:
const client = http()
.wrap(httpBodySerialize)
.wrap(httpErrorCode)
.wrap(httpJsonParser);- httpBodySerialize - Serializes init.data to request.body using JSON.stringify (e.g.
client(url, { data: { a: 1 } })). - httpErrorCode - When the request returns statusCode >= 400 throws and Error.
- httpJsonParser - Parse response body as JSON object.
Custom interceptor:
import { interceptor } from 'typed-rest-api-client';
const logger = interceptor({
init: (config) => ({ ..config, d: 10 }),
preRequest: (req, config) => {
console.log('configs:', config);
return req;
},
postRequest: async (res, config) => {
console.log('Response:', await res.text());
return res;
},
defaultConfig: {
a: -1,
b: -1,
c: -1,
d: -1
}
});
http().wrap(logger, { b: 1 })(
'https://my.domain.com',
{ config: { c: 2 } }
);
// logs configs: { a: -1, b: 1, c: 2, d: 10 }Work with other libraries
axios
import axios from 'axios';
import { http } from 'typed-rest-api-client';
const api = rest({
baseUrl: 'https://api.no-domain/',
http: (reqInfo, init) => axios({
method: init?.method ?? 'get',
url: reqInfo as string,
data: init?.data
})
});
// ... use apiAngular
@Injectable()
class RestClient {
readonly #http = inject(HttpClient);
readonly #api = rest({
http: (reqInfo, init) => firstValueFrom(
this.#http(reqInfo as string, init.data))
});
}Zod
import { z } from "zod";
const User = z.object({
username: z.string(),
});
const users = rest()('users', ({ get }) => [
get('all', {
schema: (res) => User.parse(res)
})
]);
const allUsers = await users.all(); // zod parse is applied🧱 REST Structure
rest(options)(rootPath, definition)
rest options:
baseUrl: stringhttp?: HttpHandler<object>parseArgs?: (args: Record<string, string | number | boolean>) => stringsubstituteRootParams?: (path: string, args: Record<string, string | number | boolean>) => string
Creates a namespaced REST client:
rootPath– path of the api resourcedefinition(fn)– Declaratively define methods and nested namespaces
Endpoint Creators
get(name, config?)post(name, config?)put(name, config?)patch(name, config?)del(name, config?)namespace(name, defineFn)
Each config object can include:
path– route path (e.g. /:id)args– default argument typesconfig– custom config passed to interceptorsschema- validate the response body object