工具 | 自动生成api接口

简介: 这是一个将swagger接口文档自动生成TypeScript的api接口以及interface定义。

这是一个将swagger接口文档自动生成TypeScript的api接口以及interface定义。

说明

  • 该示例的url是apifox接口文档导出的url
  • 命名:接口分组的目录需要是对应的tag

  • 参数说明:在接口请求参数中,需要按照实际规定来写,不管是在 params 还是 body 中,如果是必填项,请设置必填,这样会得到 required:true , 那么在 interface 中就没有可选,否则会得到 required:false,那么在 interface 中会有可选操作符。

  • 结果示例:

使用

  • 直接node这个文件,即可生成api文件

代码

/**
 * 说明:此文件是基于apifox创建,如果是swagger也应该是适用的,api获取是从项目设置的中的打开url获取,我这里是选择的是swagger2.0
 * fun api函数名称
 * funArr api定义的约束数组
 * oldTag 上一组tag
 * srcFolder 生产的api文件夹
 * url 需要生产api的链接
 */
const fs = require("fs");
const path = require("path");
const http = require("http");

let fun = "";
let funArr = [];
let oldTag = "";
const srcFolder = "./src/server";
const typeFolder = "./types/server";
const url = "http://127.0.0.1:4523/export/openapi?projectId=603885&version=2.0";

const mkdirsSync = (dirname) => {
  if (fs.existsSync(dirname)) {
    return true;
  } else {
    if (mkdirsSync(path.dirname(dirname))) {
      fs.mkdirSync(dirname);
      return true;
    }
  }
};

const getPath = (pathUrl) => {
  return path.resolve(__dirname, pathUrl);
};

const generateTemplate = (tagName) => {
  let str = "";
  funArr.forEach((v) => {
    str += `${v}Type,`;
  });
  return `import request from '../../request'
import {${str} } from '../../types/server/${tagName}.types'
`;
};
const generateFunc = (url, summary, type = "post") => {
  const arr = url.slice(1).split("/");
  fun = arr[arr.length - 1].replace(/^\S/, (s) => s.toUpperCase());
  funArr.push(fun);
  return `
// ${summary || ""}
export const  ${fun} = (${
    type === "get" ? `params:${fun}Type` : `data:${fun}Type`
  }):Promise<any> =>{
  return request({
    url:'${url}',
    method: '${type}',
    ${type === "get" ? "params" : "data"},
  })
}
`;
};

const generateTypeFunc = (typeName, str) => {
  return `
export interface ${typeName}Type {
${str}
}
`;
};

const httpgetJson = (url) => {
  return new Promise((resolve, reject) => {
    http
      .get(url, (res) => {
        const { statusCode } = res;
        const contentType = res.headers["content-type"];
        let error;
        if (statusCode !== 200) {
          error = new Error("请求失败。" + `状态码: ${statusCode}`);
        } else if (!contentType.includes("application/json")) {
          error = new Error(
            "无效的 content-type." +
              `期望 application/json 但获取的是 ${contentType}`
          );
        }
        if (error) {
          console.error(error.message);
          // 消耗响应数据以释放内存
          res.resume();
          return;
        }

        res.setEncoding("utf8");
        let rawData = "";
        res.on("data", (chunk) => {
          rawData += chunk;
        });
        res.on("end", () => {
          try {
            const parsedData = JSON.parse(rawData);
            resolve(parsedData);
          } catch (e) {
            reject(`错误: ${e.message}`);
          }
        });
      })
      .on("error", (e) => {
        reject(`错误: ${e.message}`);
      });
  });
};

const main = async () => {
  console.log("正在获取apifox文件...");
  const { paths } = await httpgetJson(url);
  console.log("获取成功,正在生成api文件...");
  const obj = {};
  const typeObj = {};
  for (const name in paths) {
    const path = paths[name];
    let folder = "";
    if (path.post) {
      const tag = path.post.tags[0];
      if (!tag) continue;
      const urlArray = name.slice(1).split("/");
      const typeName = urlArray[1] ? urlArray[1] : urlArray[0];
      if (name.slice(1).split("/").length === 4) {
        folder = urlArray[1];
      } else {
        if (name.slice(1).split("/")[0] !== tag) continue;
      }

      if (obj[path.post.tags[0]]) {
        obj[path.post.tags[0]].push({
          summary: path.post.summary,
          tag,
          name,
          type: "post",
          folder,
        });
      } else {
        obj[path.post.tags[0]] = [
          { summary: path.post.summary, tag, name, type: "post", folder },
        ];
      }
      if (typeObj[path.post.tags[0]]) {
        path.post.parameters.forEach((v) => {
          typeObj[path.post.tags[0]].push({
            ...v,
            tag: tag.replace(/^\S/, (s) => s.toUpperCase()),
            folder: urlArray[0],
            typeName: urlArray[1] ? urlArray[1] : urlArray[0],
          });
        });
      } else {
        typeObj[path.post.tags[0]] = [];
        path.post.parameters.forEach((v) => {
          typeObj[path.post.tags[0]].push({
            ...v,
            tag: tag.replace(/^\S/, (s) => s.toUpperCase()),
            folder: urlArray[0],
            typeName: urlArray[1] ? urlArray[1] : urlArray[0],
          });
        });
      }
    } else if (path.get) {
      const tag = path.get.tags[0];

      if (!tag) continue;
      const urlArray = name.slice(1).split("/");
      const typeName = urlArray[1] ? urlArray[1] : urlArray[0];
      if (name.slice(1).split("/").length === 4) {
        folder = urlArray[1];
      } else {
        if (name.slice(1).split("/")[0] !== tag) continue;
      }

      if (obj[path.get.tags[0]]) {
        obj[path.get.tags[0]].push({
          summary: path.get.summary,
          tag,
          name,
          type: "get",
          folder,
        });
      } else {
        obj[path.get.tags[0]] = [
          { summary: path.get.summary, tag, name, type: "get", folder },
        ];
      }
      if (typeObj[path.get.tags[0]]) {
        path.get.parameters.forEach((v) => {
          typeObj[path.get.tags[0]].push({
            ...v,
            tag: tag.replace(/^\S/, (s) => s.toUpperCase()),
            folder: urlArray[0],
            typeName: urlArray[1] ? urlArray[1] : urlArray[0],
          });
        });
      } else {
        typeObj[path.get.tags[0]] = [];
        path.get.parameters.forEach((v) => {
          typeObj[path.get.tags[0]].push({
            ...v,
            tag: tag.replace(/^\S/, (s) => s.toUpperCase()),
            folder: urlArray[0],
            typeName: urlArray[1] ? urlArray[1] : urlArray[0],
          });
        });
      }
    }
  }
  // 创建api文件
  for (const tagName in obj) {
    let jsString = "";
    const requestTypes = [];
    let folder = "";
    for (const item of obj[tagName]) {
      if (item.tag !== oldTag) {
        funArr = [];
      }
      oldTag = item.tag;
      const requestType = requestTypes.filter((o) => o === item.type);
      if (requestType.length === 0) requestTypes.push(item.type);
      jsString += generateFunc(item.name, item.summary, item.type);
      folder = item.folder;
    }
    jsString = generateTemplate(tagName) + jsString;
    mkdirsSync(getPath(`${srcFolder}/${folder}`));
    fs.writeFileSync(getPath(`${srcFolder}/${folder}/${tagName}.ts`), jsString);
  }
  // 创建types文件
  mkdirsSync(getPath(`${typeFolder}`));
  for (const typeName in typeObj) {
    let typeStr = "";
    let typeNameStr = "";
    let index = 0;
    let beginIndex = 0;
    let endIndex = 0;
    const currentTypeItemNameList = typeObj[typeName].map((v) => {
      return v.typeName;
    });
    for (const item of typeObj[typeName]) {
      console.log("item", item.name);
      typeStr += `${item.name}${item.required ? "" : "?"}:${item.type},`;
      index++;
      if (index === typeObj[typeName].length) {
        const newStrList = typeStr.split(",");
        currentTypeItemNameList.forEach((v, i) => {
          if (v != currentTypeItemNameList[i + 1]) {
            endIndex = i + 1;
            typeNameStr += generateTypeFunc(
              v.replace(/^\S/, (s) => s.toUpperCase()),
              newStrList.slice(beginIndex, endIndex).join(",\n ")
            );
            beginIndex = i + 1;
          }
        });
      }
    }

    fs.writeFileSync(
      getPath(`${typeFolder}/${typeName}.types.ts`),
      typeNameStr
    );
  }
  console.log("api文件生成完毕!");
};

main();

注:思路参考网络,加上自己思考以及功能需求,最终形成这样的版本代码!

相关文章
|
6月前
|
缓存 监控 前端开发
顺企网 API 开发实战:搜索 / 详情接口从 0 到 1 落地(附 Elasticsearch 优化 + 错误速查)
企业API开发常陷参数、缓存、错误处理三大坑?本指南拆解顺企网双接口全流程,涵盖搜索优化、签名验证、限流应对,附可复用代码与错误速查表,助你2小时高效搞定开发,提升响应速度与稳定性。
|
6月前
|
JSON 算法 API
Python采集淘宝商品评论API接口及JSON数据返回全程指南
Python采集淘宝商品评论API接口及JSON数据返回全程指南
|
6月前
|
JSON API 数据安全/隐私保护
Python采集淘宝拍立淘按图搜索API接口及JSON数据返回全流程指南
通过以上流程,可实现淘宝拍立淘按图搜索的完整调用链路,并获取结构化的JSON商品数据,支撑电商比价、智能推荐等业务场景。
|
7月前
|
JSON 前端开发 API
如何调用体育数据足篮接口API
本文介绍如何调用体育数据API:首先选择可靠服务商并注册获取密钥,接着阅读文档了解基础URL、端点、参数及请求头,然后使用Python等语言发送请求、解析JSON数据,最后将数据应用于Web、App或分析场景,同时注意密钥安全、速率限制与错误处理。
735 152
|
6月前
|
人工智能 API 开发工具
还在被复杂 API 调试工具折磨?这款开源神器救我出坑!
小华推荐开源API调试神器Yaak:离线优先、支持多协议、Git集成,告别Postman卡顿烦恼。界面清爽,一键导入,免费开源获8.5k星,10万+技术人已入坑!
395 7
|
6月前
|
人工智能 自然语言处理 测试技术
Apipost智能搜索:只需用业务语言描述需求,就能精准定位目标接口,API 搜索的下一代形态!
在大型项目中,API 数量庞大、命名不一,导致“找接口”耗时费力。传统工具依赖关键词搜索,难以应对语义模糊或命名不规范的场景。Apipost AI 智能搜索功能,支持自然语言查询,如“和用户登录有关的接口”,系统可理解语义并精准匹配目标接口。无论是新人上手、模糊查找还是批量定位,都能大幅提升检索效率,降低协作成本。从关键词到语义理解,智能搜索让开发者少花时间找接口,多专注核心开发,真正实现高效协作。
|
6月前
|
存储 缓存 算法
亚马逊 SP-API 深度开发:关键字搜索接口的购物意图挖掘与合规竞品分析
本文深度解析亚马逊SP-API关键字搜索接口的合规调用与商业应用,涵盖意图识别、竞品分析、性能优化全链路。通过COSMO算法解析用户购物意图,结合合规技术方案提升关键词转化率,助力卖家实现数据驱动决策,安全高效优化运营。
|
7月前
|
人工智能 运维 监控
阿里云 API 聚合实战:破解接口碎片化难题,3 类场景方案让业务响应提速 60%
API聚合破解接口碎片化困局,助力开发者降本增效。通过统一中间层整合微服务、第三方接口与AI模型,实现调用次数减少60%、响应提速70%。阿里云实测:APISIX+函数计算+ARMS监控组合,支撑百万级并发,故障定位效率提升90%。
547 0
|
7月前
|
JSON 自然语言处理 监控
淘宝关键词搜索与商品详情API接口(JSON数据返回)
通过商品ID(num_iid)获取商品全量信息,包括SKU规格、库存、促销活动、卖家信息、详情页HTML等。
|
7月前
|
人工智能 API 监控
告别多接口拼凑!阿里云 API 模型聚合实现技术能力协同跃迁
API聚合整合400+国内外AI模型,统一接口、屏蔽差异,降低开发与维护成本,提升效率与系统稳定性,助力开发者高效应对多API调用困境。
766 0