前言
随着前端技术的不断发展,前端已经不再是传统的展示型页面构建,而是在向着服务端的API接口服务开发越来越深入。这就需要前端开发人员具备一定的后端编程知识和技能。而随着前后端分离的逐渐成熟,我们经常需要与后端同步API接口的定义,以便前端能正确地调用后端提供的API。本文将介绍npm包@nll/api-codegen-ts的使用方法,以协助前端开发人员更加顺畅地进行API接口开发。
安装
在使用之前,我们需要先将 @nll/api-codegen-ts 安装到我们的项目中。安装方式有两种:
全局安装
在命令行中输入以下命令:
npm install -g @nll/api-codegen-ts
本地安装
在项目根目录下,打开命令行,输入以下命令:
npm install --save-dev @nll/api-codegen-ts
使用
生成客户端代码
@nll/api-codegen-ts 可以通过Swagger或OpenAPI API规范生成客户端代码。 我们可以通过运行以下命令来生成:
api-codegen-ts generate https://petstore.swagger.io/v2/swagger.json
其中https://petstore.swagger.io/v2/swagger.json
是Swagger JSON API规范URL。生成的代码将被输出到当前目录下一个名为src/api
的目录中。
生成的代码是基于TypeScript的,由以下部分组成:
src/ api/ apis/ /* 所有 API 的实现 */ models/ /* 所有模型类的实现 */ runtime/ /* 运行时代码 */ types/ /* 类型定义 */ index.ts /* 入口文件 */
使用生成的API
生成的代码包括了一系列远程API请求的封装,模型类定义,以及API使用的类型,你可以通过以下方式使用:
// 引入生成的API代码 import { DefaultApi } from './api'; // 接口实例化 const api = new DefaultApi(); // 调用接口 async function listPets() { try { const response = await api.listPets({ limit: 10 }); console.log(response.data); } catch (error) { console.error(error); } }
代码生成器配置
如果我们不想使用默认配置,可以通过运行以下命令来生成一个基本的配置文件:
api-codegen-ts init
生成出的基本配置文件如下:
{ "input": { "type": "url", "value": "" }, "output": { "targetDir": "src/api", "modelNameSuffix": "", "enumNameSuffix": "Enum", "modelModulePrefix": "", "enumModulePrefix": "", "useOptionsParameter": false }, "options": { "generateUnionEnums": true, "useSafeNames": false, "modelPropertyNaming": "original", "generateModelMethods": true, "generateApiClasses": true, "useAliasAsType": true, "useSingleRequestParameter": false, "generateModels": true, "generateEnums": true, "generateUnionTypes": true, "generateRouteTypes": false, "nestJs": false, "koaJs": false, "prefixEnumWithModelName": false } }
我们可以根据自己的需求修改配置文件,然后在运行时通过以下命令来指定配置文件:
api-codegen-ts generate -c my-config.json
示例代码
以PetStore API为例,以下是使用 @nll/api-codegen-ts 生成的代码:
/** Models **/ // Model: Category export class Category { id!: number; name?: string; } // Model: Pet export class Pet { id!: number; category?: Category; name: string; photoUrls: Array<string>; tags?: Array<Tag>; // 定义构造器 constructor(data?: Partial<Pet>) { Object.assign(this, data); } } // Model: Tag export class Tag { id!: number; name?: string; } /** APIs **/ // API: DefaultApi export class DefaultApi { private basePath: string; private axios: AxiosInstance; // 定义构造器 constructor(config?: ApiConfig) { this.basePath = config?.basePath ?? ''; this.axios = axios.create(config); } // 获取所有宠物 async listPets(queryParams?: ListPetsQueryParams, options?: ApiRequestOptions): Promise<ApiResponse<UnknownType>> { const path = `${this.basePath}/pet`; const requestConfig: AxiosRequestConfig = { method: 'get', url: path, params: queryParams ? buildUrlSearchParams(queryParams).toString() : undefined, responseType: 'json', ...options?.axiosConfig, }; try { const response = await this.axios.request(requestConfig); return asApiResponse<UnknownType>(response); } catch (error) { return asApiErrorResponse(error); } } // 创建宠物 async createPet(data: Pet, options?: ApiRequestOptions): Promise<ApiResponse<UnknownType>> { const path = `${this.basePath}/pet`; const requestConfig: AxiosRequestConfig = { method: 'post', url: path, data, responseType: 'json', ...options?.axiosConfig, }; try { const response = await this.axios.request(requestConfig); return asApiResponse<UnknownType>(response); } catch (error) { return asApiErrorResponse(error); } } // 获取特定ID的宠物 async getPetById(petId: number, options?: ApiRequestOptions): Promise<ApiResponse<UnknownType>> { const path = `${this.basePath}/pet/${petId}`; const requestConfig: AxiosRequestConfig = { method: 'get', url: path, responseType: 'json', ...options?.axiosConfig, }; try { const response = await this.axios.request(requestConfig); return asApiResponse<UnknownType>(response); } catch (error) { return asApiErrorResponse(error); } } // 更新宠物 async updatePet(data: Pet, options?: ApiRequestOptions): Promise<ApiResponse<UnknownType>> { const path = `${this.basePath}/pet`; const requestConfig: AxiosRequestConfig = { method: 'put', url: path, data, responseType: 'json', ...options?.axiosConfig, }; try { const response = await this.axios.request(requestConfig); return asApiResponse<UnknownType>(response); } catch (error) { return asApiErrorResponse(error); } } // 删除宠物 async deletePet(petId: number, apiKey?: string, options?: ApiRequestOptions): Promise<ApiResponse<UnknownType>> { const path = `${this.basePath}/pet/${petId}`; const requestConfig: AxiosRequestConfig = { method: 'delete', url: path, headers: { apiKey: apiKey, ...options?.axiosConfig?.headers, }, responseType: 'json', ...options?.axiosConfig, }; try { const response = await this.axios.request(requestConfig); return asApiResponse<UnknownType>(response); } catch (error) { return asApiErrorResponse(error); } } // 上传宠物照片 async uploadPetImage(petId: number, additionalMetadata?: string, file?: Blob, options?: ApiRequestOptions): Promise<ApiResponse<UnknownType>> { const path = `${this.basePath}/pet/${petId}/uploadImage`; const requestConfig: AxiosRequestConfig = { method: 'post', url: path, headers: { solution: 'solution-example', ...options?.axiosConfig?.headers, }, params: { additionalMetadata: additionalMetadata ?? '' }, data: file, responseType: 'json', ...options?.axiosConfig, }; try { const response = await this.axios.request(requestConfig); return asApiResponse<UnknownType>(response); } catch (error) { return asApiErrorResponse(error); } } } // ------ 分割 -------- // Helper: buildUrlSearchParams function buildUrlSearchParams(params: { [key: string]: unknown }): URLSearchParams { const searchParams = new URLSearchParams(); for (const key in params) { if (params[key] !== undefined) { searchParams.set(key, String(params[key])); } } return searchParams; } // ------ 分割 -------- // Helper: asApiResponse function asApiResponse<T>(response: AxiosResponse<T>): ApiResponse<T> { const headers = response.headers; const status = response.status; const data = response.data; return { data, headers, status }; } // ------ 分割 -------- // Helper: asApiErrorResponse function asApiErrorResponse(error: AxiosError): ApiErrorResponse { // API调用失败时,我们需要从response中,将错误信息、错误状态、请求头信息等归集到ApiErrorResponse类型中。 const headers = error.response?.headers; const status = error.response?.status ?? 0; const data = error.response?.data ?? {}; const errorResponse: ApiErrorResponse = { error: { message: error.message }, headers, status }; if (data) { try { Object.assign(errorResponse.error, { data: JSON.parse(data) }); } catch (_) { Object.assign(errorResponse.error, { data }); } } return errorResponse; } // ------ 分割 -------- // Types export type UnknownType = Record<string, unknown>; export interface ListPetsQueryParams { limit?: number; } export interface ApiConfig { basePath?: string; axiosConfig?: AxiosRequestConfig; } export interface ApiRequestOptions { axiosConfig?: AxiosRequestConfig; } export interface ApiResponse<T> { data: T; headers: Record<string, string>; status: number; } export interface ApiErrorResponse { error: { message: string; data?: any; }; headers: Record<string, string>; status: number; }
结论
在本文中,我们介绍了 @nll/api-codegen-ts 这个npm包的使用方法,其可以自动生成前端以及后端API的客户端代码、模型定义等。通过这个npm包,我们可以大大提高我们的开发效率,并且可以减少一些模板式的代码编写。同时,这个生成代码也极大地简化了前端与后端之间的接口开发。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/600673e2fb81d47349e53d81