npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@wymjs/type-safe-fetch

v1.0.5

Published

支持攔截器功能的類型安全與自動推導的 fetch

Downloads

179

Readme

@wymjs/type-safe-fetch

支持攔截器功能的類型安全與自動推導的 fetch

內部代碼很單純,就是將原生的 fetch 擴展 request, response, error 三個攔截器,其他與原生的 fetch 無任何不同

最主要的功能是提供了 fetch 的類型安全規範類型,也可以使用原生的 fetch 並覆蓋原生類型來使用

安裝

$ pnpm i @wymjs/type-safe-fetch

# 使用到 tool/params-and-body-parser 需安裝
$ pnpm i query-string

使用

這裡僅提供該庫的最佳實踐方式,詳細可查閱

// 有些內容沒寫,不是很重要,需要了解完整請查閱上方連結倉庫
// @/service/fetch2/api-type/user.ts
import { TsFetchTemplateDefineApis } from '@wymjs/type-safe-fetch'
import { ApiResponse } from '@/service/fetch2/type.ts'

// 使用 namespace 將每個 api 路徑的響應區分開來
export namespace Login {
  export type Params = {
    expiredMinutes?: number
  }
  
  export type Body = {
    username: string
    password: string
  }

  // 定義 Response 時可以使用生成工具來處理,
  // 我是使用 Jetbrains 的 Json2ts 來生成
  // 將指標點到這裡後按下右鍵,生成的彈窗勾選 type
  // 然後 Root name 輸入 Response 後按下 generate 就可以生成好類型了
  export type Response = {
    token: string
  }
}

export namespace Profile {
  export type Response = {
    id: number
    name: string
  }
}

export type Apis = TsFetchTemplateDefineApis<{
  // key 為 api 路徑,規則為 {method}:{path}
  // vscode 使用重構可以替換所有用到這 key 的值(webstorm 本來也可以的QQ)
  'get:/api/user/profile': {
    // 響應類型,此為必填,用 ApiResponse 將 Response 包裹住
    // ApiResponse 為統一的 api 響應格式
    response: ApiResponse<Profile.Response>
  }
  
  'post:/api/user/login': {
    // headers, params, body 都可以綁定類型
    // 在 fetch2() 一參傳入對應的路徑時,
    // 將會自動推斷二參有沒有什麼必傳參數沒傳的
    params?: Login.Params
    body: Login.Body
    response: ApiResponse<Login.Response>
  }
  
  // 動態路由參數,使用 : 連接即可,後續使用 pathParams 替換
  'get:/api/user/:id': {
    // ...
  }
}>



// @/service/fetch2/index.ts
import { createTsFetch, TsFetchTemplate } from '@wymjs/type-safe-fetch'
import { createMockTool } from '@wymjs/type-safe-fetch/tool/mock'
import { createMethodUrlTool } from '@wymjs/type-safe-fetch/tool/method-url'
import { createPathParamsUrlTool } from '@wymjs/type-safe-fetch/tool/path-params-url'
import { createParamsAndBodyParserTool } from '@wymjs/type-safe-fetch/tool/params-and-body-parser'
import { createMergeSameRequestTool } from '@wymjs/type-safe-fetch/tool/merge-same-request'
import { createLogTool } from '@wymjs/type-safe-fetch/tool/log'
import { envConfig } from '~env-config'
import { ApiResponse, MyListenerRequestInit, MyRequestInit } from '@/service/fetch2/type.ts'
import { commonApiErrorResponse, commonApiResponse, passAuthRequest, checkApiPermission } from '@/service/fetch2/helper/watch.ts'

const isLocal = envConfig.vite.isLocal

const fetch2 = createTsFetch() as unknown as TsFetchTemplate<
  import('@/service/fetch2/api-type/user.ts').Apis,
  MyRequestInit
>

// vite 開發運行環境下支持 @wymjs/vite-mock-apis 的功能
const mockTool = createMockTool()
// 將路徑的方法轉換成 method,如:post:/api/hello
const methodUrlTool = createMethodUrlTool()
// 將路徑參數轉換成匹配的 pathParams key-value
// 比方說:fetch2('/api/user/:id', { pathParams: { id: '1' } })
const pathParamsUrlTool = createPathParamsUrlTool()
// 將 params 轉成 querystring 以及 body 自動轉成字串傳入
const paramsAndBodyParserTool = createParamsAndBodyParserTool()
// 合併相同路徑請求,可以迴圈 call 相同路徑的 api 確認是否只 call 一次 api
const mergeSameRequestTool = createMergeSameRequestTool<
  Error,
  MyListenerRequestInit,
  ApiResponse<any>
>()
// vite 開發運行環境下 log 響應值(可選)
const logTool = createLogTool<Error, MyListenerRequestInit, ApiResponse<any>>()

// request 攔截器:泛型為 req/return 的類型
fetch2.watch.request<MyListenerRequestInit>(req => {
  if (isLocal) mockTool.transform(req)

  req.originUrl = req.url
  mergeSameRequestTool.defer(req.originUrl, req)
  methodUrlTool.transform(req)
  pathParamsUrlTool.transform(req)
  passAuthRequest(req)
  paramsAndBodyParserTool.transform(req)

  return req
})

// response 攔截器:
//   泛型 1 為 req 的類型
//   泛型 2 為 res 的類型
//   泛型 3 為 return 的類型
fetch2.watch.response<
  MyListenerRequestInit,
  Response,
  ApiResponse<any> | Promise<ApiResponse<any>>
>(async (req, res) => {
  checkApiPermission(req, res)
  const _res = await commonApiResponse(req, res)

  if (isLocal) logTool.log(req, _res)
  mergeSameRequestTool.resolve(req.originUrl, _res)

  return _res
})

// 錯誤攔截器:有寫錯誤攔截的話,除非攔截器內寫到報錯,
// 不然 fetch2() 執行後絕不會出現錯誤,就不用 try/catch 來包裹
//   泛型 1 為 error 的類型
//   泛型 2 為 req 的類型
//   泛型 3 為 res/return 的類型
fetch2.watch.error<Error, MyListenerRequestInit, ApiResponse<any> | Promise<ApiResponse<any>>>(
  async (error, req, res) => {
    const mergeResponse = await mergeSameRequestTool.waiting(req.originUrl, req, error)

    if (mergeResponse !== undefined) return mergeResponse
    if (isLocal) logTool.error(error, req)

    return commonApiErrorResponse(error, req, res)
  },
)

export { fetch2 }



// 使用
// 當你輸入一參路徑時你將會發現 IDE 彈出下拉列出所有路徑
fetch2('post:/api/user/login', {
  // body 類型未錯將會報錯
  body: {
    username: 'admin',
    password: '0000',
  },
  // params 定義為可選,所以可以不傳
  // ... 其他參數跟原生的一樣
})

fetch2('get:/api/user/:id', {
  // 動態路由參數這樣替換
  pathParams: { id: '1' },
})