顯示具有 TypeScript 標籤的文章。 顯示所有文章
顯示具有 TypeScript 標籤的文章。 顯示所有文章

2024/11/19

Auto-filling JWT Token in NestJS Swagger Documentation


import {
    DocumentBuilder,
    SwaggerModule as NestSwaggerModule
} from '@nestjs/swagger'

     // 建立Swagger的設定
        const config = new DocumentBuilder()
            .addSecurity('JWT', {
                type: 'http',
                scheme: 'bearer',
                bearerFormat: 'JWT',
                in: 'header'
            })
            .build()

        // 設定Swagger的路徑
        NestSwaggerModule.setup(path, app, document, {
            swaggerOptions: {
                // 永久儲存授權
                persistAuthorization: true,
                // 預設分類不展開
                docExpansion: 'none',
                onComplete: () => {
                    // 設定是否為登入請求
                    let isLoginRequest = false

                    // 取得fetch
                    const originalFetch = window.fetch

                    // 覆寫fetch
                    window.fetch = async function (...args) {
                        // 執行原本的fetch
                        const response = await originalFetch.apply(this, args)

                        try {
                            // 如果為SingIn Request
                            if (isLoginRequest) {
                                // clone response
                                const clone = response.clone()
                                // 取得Content-Type
                                const contentType =
                                    response.headers.get('content-type')

                                // 如果為JSON
                                if (contentType?.includes('application/json')) {
                                    // 取得JSON
                                    const data = await clone.json()

                                    // 檢查 token
                                    const token = data?.data?.token
                                    // 如果有token
                                    if (token) {
                                        // 設定 Token
                                        ;(window as any).ui.preauthorizeApiKey(
                                            'JWT',
                                            token
                                        )

                                        // 重置標記
                                        isLoginRequest = false
                                    }
                                }
                            }
                        } catch (error) {
                            console.error('Error processing response:', error)
                        }

                        return response
                    }

                    // 添加點擊事件監聽
                    ;(window as any).document.addEventListener(
                        'click',
                        (e: any) => {
                            if (
                                e.target.matches(
                                    '.btn.execute.opblock-control__btn'
                                )
                            ) {
                                // 取得URL的Query
                                const urlQuery = window.location.hash.slice(1)
                                // 如果URL的Query包含 _singIn_
                                if (urlQuery.includes('_singIn_')) {
                                    // 如果是登入請求
                                    isLoginRequest = true
                                }
                            }
                        }
                    )
                }
            },
            customSiteTitle: 'API Documentation'
        })

2024/11/16

Implement a Current Language Decorator in NestJS

再Nestjs要取得當前Request語系可以透過Decorator去取得
import { createParamDecorator, ExecutionContext } from '@nestjs/common'
import { Request } from 'express'

/**
 * 取得語系
 * @param data {unknown}
 * @example
 * @Controller('users')
 * export class UserController {
 *   @Get('profile')
 *   getProfile(@CurrentUser() userId: number) {
 *     return { userId };
 *   }
 * }
 *
 * // 與其他guard結合使用
 * @Controller('users')
 * export class UserController {
 *   @UseGuards(JwtAuthGuard)
 *   @Get('profile')
 *   getProfile(@CurrentUser() userId: number) {
 *     return { userId };
 *   }
 * }
 */
export const Lang = createParamDecorator(
    /**
     * Factory
     * @param data {unknown}
     * @param ctx {ExecutionContext}
     * @return {string}
     */
    (data: unknown, ctx: ExecutionContext): string => {
        //取得Http上下文
        const httpContext = ctx.switchToHttp()
        // 取得Request
        const request: Request = httpContext.getRequest()
        // 回傳語系 找不到就預設台灣
        return (request?.headers?.lang as string) || 'zh-TW'
    }
)

Implementing Global HTTP Status 200 Response in NestJS

根據不同的 HTTP 方法,NestJS 會自動設置對應的 HTTP 狀態碼,但基於特定需求,我會另作考量和調整
import {
    CallHandler,
    ExecutionContext,
    Injectable,
    NestInterceptor
} from '@nestjs/common'
import { Observable } from 'rxjs'
import { Response } from 'express'

/**
 * Response Interceptor
 * @class {ResponseInterceptor}
 * @implements {NestInterceptor}
 */
@Injectable()
export class ResponseInterceptor implements NestInterceptor {
    /**
     * Intercept
     * @param context {ExecutionContext} - 執行上下文
     * @param next {CallHandler} - 呼叫處理程序
     * @return {Observable} 返回 Observable
     */
    intercept(context: ExecutionContext, next: CallHandler): Observable {
        // 取得 Http 上下文
        const contextHttp = context.switchToHttp()
        // 取得 Response 物件
        const response: Response = contextHttp.getResponse()
        // 設定成Http Status 200
        response.status(200)

        return next.handle()
    }
}
main.ts

2024/11/13

Nestjs 透過Exception攔截找不到API問題

要在Nestjs攔截API或url找不到問題可以透過Exception或Middleware的方式實現
先安裝express
pnpm i @types/express express -D

代碼如下

import {
    ArgumentsHost,
    Catch,
    ExceptionFilter,
    NotFoundException
} from '@nestjs/common'
import { Request, Response } from 'express'

/**
 * Filter to catch not found exceptions
 * @class {NotFoundFilter}
 * @implements {ExceptionFilter}
 */
@Catch()
export class NotFoundFilter implements ExceptionFilter {
    /**
     * Catch the exception
     * @param exception {NotFoundException}
     * @param host {ArgumentsHost}
     * @return {Promise}
     */
    catch(
        exception: NotFoundException,
        host: ArgumentsHost
    ): Promise {
        // 取得Http的上下文
        const ctx = host.switchToHttp()
        // 取得Response
        const response = ctx.getResponse()
        //取得Request
        const request = ctx.getRequest()
        // 取得使用者語系
        const lang = request.headers['lang'] as string
        // 取得錯誤訊息
        const errorResponse = {
            msg: 'API route not found',
            err: true
        }

        // 透過Response回傳錯誤訊息
        response.status(200).json(errorResponse)
    }
}

記得要在listen前加入這段代碼

2024/11/03

NestJS 實現API版本控制

在現代API開發中,實現API版本控制是不可或缺的設計模式,特別是當應用逐步擴展且不斷迭代時,良好的版本管理至關重要。本文將介紹如何在NestJS中使用 VersioningType.URI 來實現API的版本控制,並深入分析兩種控制器的版本設定方式,幫助開發者根據實際需求選擇最佳方案。

Step 1: 設定全域前綴詞(setGlobalPrefix)

首先,設定全域前綴詞是必要的。務必先設定 setGlobalPrefix,再啟用版本控制,否則後續的版本控制將無法正確應用到API路徑。

// 設置全域的API前綴詞
const globalPrefix = 'api';
app.setGlobalPrefix(globalPrefix);

這行代碼會將所有API路徑統一設為/api開頭,例如/api/v1/test/echo。這樣可以讓API結構層次分明,並方便後續的維護和版本管理。

Step 2: 啟用版本控制(enableVersioning)

使用NX 體驗 Monorepo的美好

什麼是 Monorepo?為什麼要使用它?

隨著軟體專案的成長,特別是在多模組、多應用程式的環境下,管理代碼的挑戰也隨之增加。傳統的 Multi-Repo 策略(每個模組使用獨立儲存庫)在版本一致性和依賴管理方面存在不少痛點,特別是當需要頻繁更新和協作時,容易引發衝突和整合困難。

Monorepo 是一種更集中的管理方式。透過將所有模組放在一個儲存庫中,開發團隊可以更容易地維持一致的依賴版本和共享代碼。這種方法在大型專案中具備幾個顯著優勢:

  • 依賴一致性:Monorepo 確保所有模組使用相同版本的依賴,避免因為版本衝突而引起的問題。
  • 跨模組重用和同步:允許模組之間共享代碼,並在單一提交中完成多個模組的變更,方便同步更新。
  • 統一的 CI/CD 管理:可以使用一套測試和構建流程來處理所有模組,簡化 CI/CD 配置。
  • 增量編譯和快取:通過增量構建和快取技術(如 NX 的增量編譯),Monorepo 能顯著減少重複編譯的時間,提高開發效率。

NX:專為 Monorepo 優化的開發工具

NX 是一個強大的 Monorepo 工具,專為管理多模組、多應用的專案而設計。它支援增量編譯、自動快取、依賴視覺化等功能,幫助開發團隊減少不必要的構建,提升整體開發效率。此外,NX 還具有任務調度、代碼生成和豐富的插件生態,適合大型團隊的專案開發需求。

此外,NX 可以與高效的包管理器 pnpm 搭配使用。pnpm 具備高度優化的快取和依賴管理能力,使得構建和測試更加高效。接下來,我們將介紹如何使用 NX 和 pnpm 建立並管理一個 Monorepo 專案,並說明其主要操作指令。