iT邦幫忙

2021 iThome 鐵人賽

DAY 21
0
Modern Web

NestJS 帶你飛!系列 第 21

[NestJS 帶你飛!] DAY21 - HTTP Module

本系列文已出版成書「NestJS 基礎必學實務指南:使用強大且易擴展的 Node.js 框架打造網頁應用程式」,感謝 iT 邦幫忙與博碩文化的協助。如果對 NestJS 有興趣、覺得這個系列文對你有幫助的話,歡迎前往購書,你的支持是我最大的寫作動力!

很多時候我們會需要去串接第三方的 API,例如:綠界科技的金流服務、Binance 的 API 等,這時候如果第三方沒有提供相關的 SDK 讓我們使用的話,就必須自己用 HTTP Request 去存取對應的資料,早期的 node.js 開發人員可能會使用 request 來實作,但該函式庫現在已經被棄用了,取而代之的是 node-fetchaxios,而 Nest 內建了 HTTP Module,它是基於 axios 進行包裝的模組,讓 Nest 開發人員不必為使用哪個套件煩惱,HTTP Module 即裝即用!

注意:在第 8 版後的 NestJS 已經將 HTTP Module 獨立成 @nestjs/axios 套件,若使用第 8 版後的 NestJS 需要另外使用 npm install @nestjs/axios 進行安裝。

使用 HTTP Module

HTTP Module 的 class 名稱為 HttpModule,它匯出了一個 HttpService 的 Service,其提供 axios 的方法來處理 HTTP 請求,並且使用 Observable 的形式。

注意:想要知道 axios 具體有哪些方法可以查看官方說明

app.module.ts 為例,將 HttpModule 導入:

import { HttpModule, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [HttpModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

這裡我們借用一下 JSONPlaceholder 做為第三方 API,並使用 todos 的資源,將其資料結構用 interface 的方式存在 src/common/models 資料夾中的 todo.model.ts

export interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

調整一下 app.service.ts 的內容,透過 getTodos 方法去取得 todos 的資源:

注意:由於 Agent 的問題,這裡我們需要配置 httpsAgentrejectUnauthorizedfalse 以正常使用此 API。

import { HttpService, Injectable } from '@nestjs/common';

import { Agent } from 'https';

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { Todo } from './common/models/todo.model';

@Injectable()
export class AppService {

  constructor(
    private readonly http: HttpService
  ) {}

  getTodos(): Observable<Todo> {
    const httpsAgent = new Agent({ rejectUnauthorized: false });
    return this.http.get('https://jsonplaceholder.typicode.com/todos', { httpsAgent }).pipe(
      map((res) => res.data)
    );
  }

}

調整 app.controller.ts,在 getTodos 中調用 AppServicegetTodos

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/todos')
  getTodos() {
    return this.appService.getTodos();
  }

}

透過 Postman 存取 http://localhost:3000/todos,會得到下方結果:
https://ithelp.ithome.com.tw/upload/images/20210610/20119338wKp4p3fw9d.png

預設 axios 配置

在上述範例中,會發現存取 JSONPlaceholder 的 todos 資源會碰到 Agent 的問題,如果有多個 API 都會碰到此問題,又不希望每次都要重複寫一樣的東西,這時候就可以運用 HttpModuleregister 方法來配置預設值,而這個預設值可以配置的項目與 HttpService 各方法中的 options 相同,具體的內容可以參考官方說明

這裡以 app.module.ts 為例,將 Agent 的配置設為預設值:

import { HttpModule, Module } from '@nestjs/common';

import { Agent } from 'https';

import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    HttpModule.register({
      httpsAgent: new Agent({ rejectUnauthorized: false })
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

接著調整 app.service.ts,將本來配置好的 Agent 配置移除:

import { HttpService, Injectable } from '@nestjs/common';

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { Todo } from './common/models/todo.model';

@Injectable()
export class AppService {

  constructor(
    private readonly http: HttpService
  ) {}

  getTodos(): Observable<Todo> {
    return this.http.get('https://jsonplaceholder.typicode.com/todos').pipe(
      map((res) => res.data)
    );
  }

}

最後,透過 Postman 存取 http://localhost:3000/todos,會成功獲取 todos 的資料:
https://ithelp.ithome.com.tw/upload/images/20210610/20119338vvfPoSXwoY.png

使用環境變數

HttpModule 有提供 registerAsync 方法,透過這個方法可以添加依賴的 Provider,並用工廠函式將其值帶入 HttpModule,運用這樣的機制來注入 ConfigService,進而將要配置的預設值帶入。

這邊來做個簡單的範例,先在專案目錄下新增 .env 並添加下方的環境變數:

HTTP_TIMEOUT=3000

修改 app.module.ts 的內容,在 registerAsync 匯入 ConfigModule 並在 injects 帶入 ConfigService,最後在 useFactory 注入 ConfigService

import { HttpModule, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';

import { Agent } from 'https';

import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true
    }),
    HttpModule.registerAsync({
      imports: [ConfigModule],
      useFactory: (config: ConfigService) => ({
        httpsAgent: new Agent({ rejectUnauthorized: false }),
        timeout: config.get('HTTP_TIMEOUT')
      }),
      inject: [
        ConfigService
      ]
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

這樣就可以順利將環境變數帶入至 HttpModule 來配置預設值了!

小結

Nest 將 Http Request 相關功能直接打包成內建模組實在很方便,不必再花時間去選擇要使用哪個套件,也不必花時間將套件包裝成 Nest 的模組,直接注入 HttpService 即可使用。這裡附上今天的懶人包:

  1. HttpModule 為內建模組,透過注入 HttpService 來使用 axios 的方法。
  2. 可以透過 HttpModuleregister 來配置預設值。
  3. 可以透過 HttpModuleregisterAsync 來取得注入 Provider 的值,進而配置預設值。

上一篇
[NestJS 帶你飛!] DAY20 - File Upload
下一篇
[NestJS 帶你飛!] DAY22 - MongoDB
系列文
NestJS 帶你飛!32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言