nestjs-httyped-client
TypeScript icon, indicating that this package has built-in type declarations

0.5.4 • Public • Published

nestjs-httyped-client

Give your NestJS controller a nice, clean HTTP client with zero additional code 🎉 !

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?

  • Http client
    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
            });
        } 
    }
  • NestJS controller
    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.

  • Http client
    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 {
    }
  • NestJS controller
    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 the abstract 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 helping httyped-client select the appropriate response mapper.
  • 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)

Readme

Keywords

none

Package Sidebar

Install

npm i nestjs-httyped-client

Weekly Downloads

2

Version

0.5.4

License

MIT

Unpacked Size

460 kB

Total Files

16

Last publish

Collaborators

  • nicolas.thierion