几周前,我终于有时间开始try NodeJS API构建,经过一些研究后,我决定使用Express + Sequelize + PostgreSQL + OpenAPI的组合. 此时我跳过了TypeScript, Select 了JS ES6+. 在阅读了OpenAPI Specification和我正在使用的npm包的官方文档(express-openapi)之后,我一直在从openapi 3.x到swagger 2.0来回切换,并将api—doc从JSON切换到yml,但最后我总是在同一点上遇到这个错误:

SyntaxError: Invalid regular expression: /^\/v2\/users\(?:([^\/]+?))\/?$/i: Unmatched ')'
    at new RegExp (<anonymous>)
    at pathtoRegexp (...\express-sequelize-openapi\node_modules\path-to-regexp\index.js:128:10)
    at new Layer (...\express-sequelize-openapi\node_modules\express\lib\router\layer.js:45:17)
    at Function.route (...\express-sequelize-openapi\node_modules\express\lib\router\index.js:505:15)
    at app.<computed> [as get] (...\express-sequelize-openapi\node_modules\express\lib\application.js:498:30)
    at Object.visitOperation (...\express-sequelize-openapi\node_modules\express-openapi\dist\index.js:131:33)
    at ...\express-sequelize-openapi\node_modules\openapi-framework\dist\index.js:370:29
    at Set.forEach (<anonymous>)
    at ...\express-sequelize-openapi\node_modules\openapi-framework\dist\index.js:233:100
    at Array.forEach (<anonymous>)

以下是我在当前版本的package. json中使用的一些依赖项:

    "body-parser": "^1.20.2",
    "cors": "^2.8.5",
    "express": "^4.19.1",
    "express-openapi": "^12.1.3",
    "helmet": "^7.1.0",
    "pg": "^8.11.3",
    "pg-hstore": "^2.3.4",
    "sequelize": "^6.37.1",
    "sequelize-cli": "^6.6.2",
    "swagger-ui-express": "^5.0.0",

下面是app.js的内容:

import express from "express";
import bodyParser from "body-parser";
import cors from "cors";
import helmet from "helmet";
import path from "path";
import swaggerUi from "swagger-ui-express";
import { initialize as initializeOpenApi } from "express-openapi";
import { fileURLToPath } from "url";
import v1ApiDoc from "./doc/api-doc-v2.js";

// Defining the Express app
const app = express();

// Adding Helmet to enhance your Rest API's security
app.use(helmet());

// Using bodyParser to parse JSON bodies into JS objects
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// Enabling CORS for all requests
app.use(cors());

const docsPath = "/openapi.json";

// OpenAPI UI
app.use("/api-documentation", swaggerUi.serve, swaggerUi.setup(v1ApiDoc));

export const startApp = async (port) => {
  // OpenAPI routes
  const openApiDoc = await initializeOpenApi({
    apiDoc: v1ApiDoc,
    app,
    docsPath,
    paths: "./api/routes/v2",
  });

  app.use(function (err, req, res, next) {
    res.status(err.status).json(err);
  });

  // Starting the server
  app.listen(port => {
      console.log(`Listening on port ${port}`);
  });
};

以下是我创建的当前api—doc v2:

const apiDoc = {
  swagger: "2.0",
  info: {
    title: "Node Express API.",
    version: "0.1.0",
  },
  basePath: "/v2",
  paths: {},
  tags: [
    {
      name: "Users",
    },
  ],
  definitions: {
    GeneralSuccess: {
      type: "object",
      properties: {
        success: {
          type: "boolean",
        },
        data: {
          type: "object",
        },
      },
    },
    GeneralError: {
      type: "object",
      properties: {
        success: {
          type: "boolean",
        },
        error: {
          type: "object",
          properties: {
            message: {
              type: "string",
            },
            error: {
              type: "object",
            },
          },
        },
      },
    },
    User: {
      required: ["id"],
      type: "object",
      properties: {
        id: {
          type: "string",
          description: "User's unique identifier",
        },
        email: {
          type: "string",
          description: "User's email address",
        },
        password: {
          type: "string",
          description: "User's password",
        },
        firstName: {
          type: "string",
          description: "User's firstname",
        },
        lastName: {
          type: "string",
          description: "User's lastname",
        },
      },
    },
  },
  responses: {
    200: {
      // Success
      description: "Successful request.",
      schema: {
        $ref: "#/definitions/GeneralSuccess",
      },
    },
    404: {
      // Not Found
      description: "Entity not found.",
      schema: {
        $ref: "#/definitions/GeneralError",
      },
    },
    422: {
      // Illegal Input
      description: "Illegal input for operation.",
      schema: {
        $ref: "#/definitions/GeneralError",
      },
    },
    500: {
      // Internal Server Error
      description: "An unexpected error occurred",
      schema: {
        $ref: "#/definitions/GeneralError",
      },
    },
    default: {
      description: "Unexpected error",
      schema: {
        $ref: "#/definitions/GeneralError",
      },
    },
  },
};

export default apiDoc;

路由是在folder/routes/v2下指定的. 到目前为止,我有两个路由,users.js直接在干线下工作(get,post)和users/{id}. js每次都会触发相同的错误.在这一点上的内容是无关紧要的,因为我直接在v2下复制/粘贴相同的代码到不同的文件中,并且它工作正常.

所以我在这一点上得到的更多的是一个配置问题,一些愚蠢的细节我遗漏了,或者是一个包版本冲突.

我很感激我能得到的任何建议,让这个项目运行.

PS:我认为有一个次要问题是,我无法将任何端点加载到本地SwaggerUI api—documentation页面.

推荐答案

这是Open—API代码中的一个bug:https://github.com/kogosoftwarellc/open-api/issues/896

问题是,某个版本之后的依赖项glob工作方式不同,这就expose 了这个bug.

最好的解决方案可能是设置glob的版本7:npm equivalent of yarn resolutions?

Node.js相关问答推荐

MongoServelSelection错误:服务器 Select 在30000 ms后超时

将 POST 的 json 变量格式化为 lambda

如何设置 Puppeteer Select 器的唯一性?

如何在 JavaScript 中显示多维数组中使用的一维数组的变量名?

Amplify 部署的应用程序出现TypeError: handler is not a function错误,但它在本地运行

Cloudflare 522 错误 - javascript 客户端连接到 node 服务器

module.exports=require('other') 和临时变量有什么区别?

处理 UTC 日期和future

在新创建的 Angular 工作区上运行 ng lint 时出错

使用Typescript 时我应该避免循环导入吗?

将已保存的卡片从条带显示到前端

Sharp JS 依赖关系 destruct 了 Elastic Beanstalk 上的 Express Server

Socket IOFlutter 未连接

您如何写入 aws lambda 实例的文件系统?

什么是nestjs错误处理方式(业务逻辑错误vs.http错误)?

AWS Kinesis 中的分区键是什么?

与 NPM 一起安装时找不到 Express 模块

如何从 Redis 保存和检索会话

对不同对象中的函数使用相同的键时,V8 中的函数调用缓慢

Node.js 中的 PHP exit()/die() 类似功能是什么