nestjs-httyped-client
v0.5.3
Published
An AOP powered typesafe http client for your NestJS controllers, inspired by Retrofit library for java
Downloads
7
Readme
nestjs-httyped-client
![npm version] ![license] ![NPM Downloads] ![bundlejs]
🎉 Demo
See the demo on Stackblitz.
💡 Why?
Maintaining an HTTP client every time your web API changes can be tedious and error-prone. Why should a NestJS controller and its corresponding client have completely different codebases when they share the same API?
interface User {
id: string;
name: string;
}
export class UserClient {
async getAll(name: string): Promise<User[]> {
return fetch(`${baseUrl}/users?name=${name}`);
}
async getById(id: number): User {
return fetch(`${baseUrl}/users/${id}`);
}
createOne(user: User): void {
return fetch(`${baseUrl}/users`, {
method: 'POST',
body: user
});
}
}
import {
Controller,
Get,
Post,
Query,
Param,
Body
} from `@nestjs/common`
interface User {
id: string;
name: string;
}
@Controller('users')
export class UserController {
@Get()
getAll(@Query('name') name?: string): User[] {
//...
}
@Get(':id')
getById(@Param('id') id: number): User {
//...
}
@Post()
createOne(@Body() user: User): void {
//...
}
}
Inspired by the retrofit java library,
nestjs-httyped-client
use the same decorators from your existing NestJS controller to give you a corresponding implementation of an http client.
By leveraging AspectJS and the httyped-client
, this library simplifies the creation of synchronized clients, reducing redundancy and ensuring your client always reflects your server-side API.
import {
Get,
Post,
Query,
Param,
Body
} from `@aspectjs/nestjs/common`
import { abstract } from `@aspectjs/common/utils`
interface User {
id: string;
name: string;
}
export abstract class UsersApi {
@Get()
getAll(@Query('name') name?: string): User[] {
return abstract([User])
}
@Get(':id')
getById(@Param('id') id: number): User {
return abstract(User)
}
@Post()
createOne(@Body() user: User): void {
return abstract<void>()
}
}
@HttypedClient('users')
export class UserClient extends UserApi {
}
import {
Get,
Post,
Query,
Param,
Body
} from `@aspectjs/nestjs/common`
@Controller('users')
export class UserController extends UserApi {
getAll(@Query('name') name?: string): User[] {
return
}
getById(@Param('id') id: number): User {
//...
}
createOne(@Body() user: User): void {
//...
}
}
🚀 Getting started:
Install the packages.
npm i @aspectjs/core @aspectjs/common @aspectjs/nestjs nestjs-httyped-client
To use on nodeJS, you may also need a implementation for the fetch api:
npm i whatwg-fetch
Create an empty API class, using only the annotations from
@aspectjs/nestjs/common
and theabstract
placeholder from@aspectjs/common/utils
.// users.api.ts import { Get, Post, Query, Param, Body } from `@aspectjs/nestjs/common` export abstract class UsersApi { @Get() getAll(@Query('name') name?: string): User[] { return abstract([User]) } @Get(':id') getById(@Param('id') id: number): User { return abstract(User) } @Post() createOne(@Body() user: User): void { return abstract<void>() } }
Note:
- Annotations from the package
@aspectjs/nestjs/common
share the same signature as those from@nestjs/common
. - TypeScript does not support decorators on interfaces. Instead, we use abstract classes. The
abstract()
value returned by the method serves as a placeholder, allowing TypeScript to properly infer the actual return type and helpinghttyped-client
select the appropriate response mapper.
- Annotations from the package
Create an Http client class, annotated with the
@NestClient()
annotation, that extends the api class:// users.client.ts import { NestClient } from 'nest-client'; @NestClient('users') export class UsersClient extends UsersApi { }
Let your NestJS controller extend the API class
// users.controller.ts import { Controller, Get, Post, Query, Param, Body } from `@aspectjs/nestjs/common` @Controller('users') export class UsersController extends UsersApi { @Get() getAll(@Query('name') name?: string): User[] { // ... } @Get(':id') getById(@Param('id') id: number): User { // ... } @Post() createOne(@Body() user: User): void { // ... } }
On your server code, enable the
NestClient
aspect to give annotations from@aspectjs/nestjs/common
the same behavior as the original decorators.
import 'whatwg-fetch';
import { getWeaver } from '@aspectjs/core';
import { NestClientAspect } from 'nestjs-httyped-client';
getWeaver().enable(new NestClientAspect());
Great! Now, you're ready to use the http client created with @NestClient
:
const usersClient = new NestClientFactory({
baseUrl: 'http://localhost:3000',
})
.addRequestHandler((r) => console.log(`[${r.method}] ${r.url}`))
.create(UsersClient);
await usersClient.getById(1)