Skip to content

boy672820/nestjs-httpf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nestjs-httpf

Functional-style HTTP client for NestJS

npm version License: MIT

Features

  • Functional Style: Handle complex API requests safely and declaratively
  • Language-Friendly: Promise-based instead of Observable, enabling direct use of async/await
  • AsyncIterable-Based: Leverages FxTS's AsyncIterable for efficient data processing through lazy evaluation and function composition
  • Retry Support: Automatically retry failed requests
  • Error Handling: Use catchError to treat errors as values instead of try-catch
  • FxTS Integration: Access various helper functions from the FxTS library
  • Got-Based: Uses the powerful and reliable Got HTTP client

Background

NestJS's built-in HTTP module provides an excellent way to safely handle complex API processing by applying RxJS's reactive programming. However, since it returns RxJS Observable objects, you need to extract values through operators like firstValueFrom, which can be cumbersome.

nestjs-httpf solves this inconvenience and allows you to handle asynchronous operations in a more language-friendly way:

  • âś… Directly work with Promises, enabling await usage
  • âś… Write asynchronous processing declaratively with functional style
  • âś… Compose functions with the FxTS library for various features

Installation

npm install nestjs-httpf @fxts/core

or

yarn add nestjs-httpf @fxts/core

or

pnpm add nestjs-httpf @fxts/core

Quick Start

1. Register the Module

import { Module } from '@nestjs/common';
import { HttpfModule } from 'nestjs-httpf';

@Module({
  imports: [HttpfModule],
})
export class AppModule {}

2. Use in a Service

import { Injectable } from '@nestjs/common';
import { HttpfService } from 'nestjs-httpf';

@Injectable()
export class UserService {
  constructor(private readonly httpfService: HttpfService) {}

  async getUser(id: string) {
    const user = await this.httpfService
      .get<User>(`https://api.example.com/users/${id}`)
      .chain(pluck('body'))
      .head();

    return user;
  }
}

Usage Examples

Basic HTTP Requests

// GET request
const data = await this.httpfService
  .get<{ message: string }>('https://api.example.com/hello')
  .chain(pluck('body'))
  .head();

// POST request
const result = await this.httpfService
  .post<{ id: string }>('https://api.example.com/users', {
    json: { name: 'John', email: 'john@example.com' },
  })
  .chain(pluck('body'))
  .head();

// PUT request
await this.httpfService.put('https://api.example.com/users/123', {
  json: { name: 'Jane' },
});

// PATCH request
await this.httpfService.patch('https://api.example.com/users/123', {
  json: { email: 'jane@example.com' },
});

// DELETE request
await this.httpfService.delete('https://api.example.com/users/123');

Error Handling

Use catchError to treat errors as values:

const result = await this.httpfService
  .get<User>('https://api.example.com/users/123')
  .catchError((error) => ({
    body: null,
    statusCode: 500,
    error: error.message,
  }))
  .map((response) => response.body)
  .head();

// Transform and handle errors
const user = await this.httpfService
  .get<User>('https://api.example.com/users/123')
  .catchError((error) => {
    console.error('Failed to fetch user:', error);
    return { body: { id: '0', name: 'Unknown' } };
  })
  .map((response) => response.body)
  .head();

Retry

Automatically retry failed requests:

// Retry up to 3 times
const data = await this.httpfService
  .get<Data>('https://api.example.com/unstable-endpoint')
  .retry(3)
  .map((response) => response.body)
  .head();

// Using retry with catchError
const result = await this.httpfService
  .get<Data>('https://api.example.com/data')
  .retry(2)
  .catchError((error) => ({
    body: { fallback: true },
    statusCode: 200,
  }))
  .map((response) => response.body)
  .head();

Using FxTS Methods

You can use various helper functions from FxTS:

// filter: Process only responses that match conditions
const successResponse = await this.httpfService
  .get<Data>('https://api.example.com/data')
  .filter((response) => response.statusCode === 200)
  .map((response) => response.body)
  .head();

// take: Get only the first N items
const items = await this.httpfService
  .get<Item[]>('https://api.example.com/items')
  .map((response) => response.body)
  .take(5)
  .toArray();

// Complex chaining
const processedData = await this.httpfService
  .get<RawData>('https://api.example.com/data')
  .retry(2)
  .catchError(() => ({ body: [] }))
  .filter((response) => response.statusCode === 200)
  .map((response) => response.body)
  .map((data) => data.map((item) => ({ ...item, processed: true })))
  .head();

Flattening Data with mergeMap

const allItems = await this.httpfService
  .get<{ items: Item[] }>('https://api.example.com/data')
  .map((response) => response.body)
  .mergeMap((data) => data.items)
  .toArray();

Advanced Configuration

Global Configuration

import { Module } from '@nestjs/common';
import { HttpfModule } from 'nestjs-httpf';

@Module({
  imports: [
    HttpfModule.register({
      global: true,
      timeout: 5000,
      retry: {
        limit: 2,
      },
      headers: {
        'User-Agent': 'my-app/1.0.0',
      },
    }),
  ],
})
export class AppModule {}

Async Configuration

import { Module } from '@nestjs/common';
import { HttpfModule } from 'nestjs-httpf';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    HttpfModule.registerAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        timeout: configService.get('HTTP_TIMEOUT'),
        headers: {
          'API-Key': configService.get('API_KEY'),
        },
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

Configuration with useClass

import { Injectable } from '@nestjs/common';
import { HttpfModuleOptionsFactory, HttpfModuleOptions } from 'nestjs-httpf';

@Injectable()
class HttpConfigService implements HttpfModuleOptionsFactory {
  createHttpOptions(): HttpfModuleOptions {
    return {
      timeout: 5000,
      retry: {
        limit: 3,
      },
    };
  }
}

@Module({
  imports: [
    HttpfModule.registerAsync({
      useClass: HttpConfigService,
    }),
  ],
})
export class AppModule {}

API Reference

HttpfService

Methods

  • get<T>(url: string | URL, options?: OptionsOfJSONResponseBody): HttpfAsyncIterable<Response<T>>
  • post<T>(url: string | URL, options?: OptionsOfJSONResponseBody): HttpfAsyncIterable<Response<T>>
  • put<T>(url: string | URL, options?: OptionsOfJSONResponseBody): HttpfAsyncIterable<Response<T>>
  • patch<T>(url: string | URL, options?: OptionsOfJSONResponseBody): HttpfAsyncIterable<Response<T>>
  • delete<T>(url: string | URL, options?: OptionsOfJSONResponseBody): HttpfAsyncIterable<Response<T>>
  • head<T>(url: string | URL, options?: OptionsOfJSONResponseBody): HttpfAsyncIterable<Response<T>>

HttpfAsyncIterable

HttpfAsyncIterable provides all FxTS methods along with the following additional methods:

Additional Methods

  • catchError<E>(handler: (error: unknown) => E): HttpfAsyncIterable<T | E> - Catch errors and return a fallback value
  • retry(retries: number): HttpfAsyncIterable<T> - Retry the specified number of times on failure
  • mergeMap<U>(mapper: (value: T) => Iterable<U>): HttpfAsyncIterable<U> - Flatten values

FxTS Methods and Helper Functions

See the FxTS documentation for more information.

License

This project is licensed under the MIT License.

Third-Party Licenses

This project uses the following third-party libraries:

Library License
@fxts/core Apache-2.0
got MIT
@nestjs/common MIT

Apache-2.0 License Notice

This project depends on @fxts/core which is licensed under the Apache License 2.0. When using this library, you must comply with the Apache-2.0 license terms for @fxts/core, which include:

  • Attribution: You must give appropriate credit and include a copy of the Apache-2.0 license
  • State Changes: If you modify @fxts/core, you must state the changes made
  • NOTICE Preservation: If @fxts/core includes a NOTICE file, you must include it in your distribution
  • Patent Grant: Apache-2.0 includes an express grant of patent rights from contributors

For the full Apache-2.0 license text, see: https://www.apache.org/licenses/LICENSE-2.0

Contributing

Issues and pull requests are always welcome!

Related Projects

  • NestJS - Progressive Node.js framework
  • Got - Human-friendly and powerful HTTP request library
  • FxTS - Functional library for TypeScript/JavaScript

About

Functional-style HTTP client for NestJS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published