如何使用Swagger记录Node.js的REST API

255 阅读9分钟

使用Swagger记录Node.js的REST API

Swagger是一个用于设计、构建、记录和使用RESTful APIs的软件工具。它遵循OpenAPI规范。

API(应用编程接口)是一个中介,它使两个不同的软件应用程序能够相互沟通。

OpenAPI规范是一个用于创建描述、生产、消费和可视化RESTful APIs的接口的规范。它也被称为swagger规范。

目标

在这篇文章中,我们将使用swagger记录一个简单的Node.js REST(Representational State Transfer)API。

前提条件

要跟上本文的进度,必须具备以下条件。

  • 在你的电脑上安装[Node.js]。
  • 一些JavaScript的基本知识。
  • 熟悉使用[Express.js]构建REST APIs。

简介

Swagger在开发API的文档时依赖于规范。这些规范可以是YAMLJSON 格式。

YAML(YAML Ain't Markup Language)是一种用于编写配置文件的数据序列化标准。

JSON(JavaScript Object Notation)是一种轻量级的数据序列化标准,遵循JavaScript对象语法。数据被安排在键/值对中。

在这篇文章中,我们将使用JSON 格式实现规范。通过这种格式,我们将从.js 文件。

设置开发服务器

由于主要重点是记录REST API,我们不会处理创建一个,但我们将从这个GitHub仓库克隆一个。

该API将使用以下依赖项。

  • [lowdb]:用于存储数据。
  • [morgan]:用于记录请求。
  • [nanoid]:用于生成ID。
  • [cors]:用于设置跨源策略。
  • [swagger-ui-express]:用于在我们的API中提供swagger用户界面。
  • [nodemon]:用于在有任何变化的情况下重新启动开发服务器。

为了安装这些依赖项,我们需要在项目的根目录下运行以下命令。

npm install

安装完依赖项后,你可以通过运行以下命令来启动开发服务器。

npm run dev

现在,开发服务器已经设置好了,我们的重点是记录API。我鼓励你浏览一下API,了解各种端点以及数据是如何交换的。

在这一点上,你可以使用[postman]。

从这里开始,我们将集中讨论src 文件夹中的docs

记录API的一般信息

API的一般信息包括openAPI版本openapi ,以及info 对象下的API的具体信息。

info 对象包括title,description,version,contact 等信息。

这些信息对于公开的API来说是非常值得推荐的,以提高开发者的体验。

在我们的API中,我们将记录docs/basicInfo.js 文件中的一般信息。

我们将包括但不限于以下信息。

module.exports = {
  openapi: "3.0.3", // present supported openapi version
  info: {
    title: "Simple Todos API", // short title.
    description: "A simple todos API", //  desc.
    version: "1.0.0", // version number
    contact: {
      name: "John doe", // your name
      email: "john@web.com", // your email
      url: "web.com", // your website
    },
  },
};

在写这篇文章的时候,openapi 的最新支持版本是3.0.3 。请确保将其修改为最新支持的版本。

由于我们的API很简单,所以info 对象下的信息不多。如果你有一个复杂的API,请随意添加更多的信息。

为了测试上述内容。

  • 确保开发服务器从你的终端启动并运行。
  • 在你的浏览器中,从http://localhost:4000/api-docs ,打开你的文档页面。
  • 如果出现错误,请重新审视这些步骤。

否则,你的网页应该像下面这样。

general_info_screenshot

一个关于API的一般信息的屏幕截图

记录API服务器

根据当前API的环境,它将在不同的服务器上运行。

当处于开发环境时,API在本地服务器上运行。当处于测试环境时,它在测试服务器上,而当处于生产环境时,它在生产服务器上。

在编写文档时,你要根据环境指定API所运行的不同服务器。

在我们的API中,我们将通过编辑docs/servers.js ,在开发环境中记录它,如下所示。

module.exports = {
  servers: [
    {
      url: "http://localhost:4000/todos", // url
      description: "Local server", // name
    },
  ],
};

由于我们只在开发环境下操作这个API,我们只指定了本地服务器。如果,你要推送到测试和生产环境,请确保你包括这些信息。它遵循相同的格式。

为了测试这个。

  • 确保你的开发服务器已经启动并运行。
  • 在你的浏览器中,刷新文档页面。
  • 如果有任何错误,请重新审视这些步骤。

否则,以下部分应该被添加到你的页面中。

servers-screenshot

API服务器的屏幕截图

记录API标签

标签用于对不同的相关操作进行分组。例如,在一个处理用户和商店的API中,你可以使用标签来区分它们各自的操作。

在我们的API中,由于我们只处理todos ,我们将只在docs/tags.js 文件中添加一个标签,如下。

module.exports = {
  tags: [
    {
      name: "Todo CRUD operations", // name of a tag
    },
  ],
};

在一个更复杂的API中,你有不同的当事人,你可以像上面那样在标签数组中添加每个当事人。

为了测试这一点。

  • 确保开发服务器正在运行。
  • 在你的浏览器中,刷新文档页面。
  • 如果出现错误,重温一下步骤。

否则,这部分应该被添加到你的页面中。

tags_screenshot

API标签的屏幕截图

记录API组件

组件是用于容器化不同的可重用定义的。可重用的定义涉及到模式、参数、安全模式、requestBodies、响应、标头、示例、链接和回调。在它们的定义之后,组件可以使用$ref

在我们的API中,我们通过编辑docs/components.js 文件来记录组件,如下所示。

module.exports = {
  components: {
    schemas: {
      // id model
      id: {
        type: "string", // data type
        description: "An id of a todo", // desc
        example: "tyVgf", // example of an id
      },
      // todo model
      Todo: {
        type: "object", // data type
        properties: {
          id: {
            type: "string", // data-type
            description: "Todo identification number", // desc
            example: "ytyVgh", // example of an id
          },
          title: {
            type: "string", // data-type
            description: "Todo's title", // desc
            example: "Coding in JavaScript", // example of a title
          },
          completed: {
            type: "boolean", // data type
            description: "The status of the todo", // desc
            example: false, // example of a completed value
          },
        },
      },
      // Todo input model
      TodoInput: {
        type: "object", // data type
        properties: {
          title: {
            type: "string", // data type
            description: "Todo's title", // desc
            example: "Coding in JavaScript", // example of a title
          },
          completed: {
            type: "boolean", // data type
            description: "The status of the todo", // desc
            example: false, // example of a completed value
          },
        },
      },
      // error model
      Error: {
        type: "object", //data type
        properties: {
          message: {
            type: "string", // data type
            description: "Error message", // desc
            example: "Not found", // example of an error message
          },
          internal_code: {
            type: "string", // data type
            description: "Error internal code", // desc
            example: "Invalid parameters", // example of an error internal code
          },
        },
      },
    },
  },
};

由于我们的API没有那么复杂,我们只在组件中加入了schemas

schemas ,我们定义了idTodoTodoInput 、和Error 。对于每个模式的定义,我们都使用键来描述其各自的数据,如type,description,example, 和properties

为了测试这一点。

  • 确保开发服务器正在运行。
  • 在你的浏览器中,刷新文档页面。
  • 如果出现错误,重温一下步骤。

以下部分应该被添加到文档页面中。

schemas_screenshot

API组件的屏幕截图

记录API的路径

路径是指要访问的路线。根据操作方法和传递的数据,每条路径都是不同的。

为了记录我们API中的路径,我们将分别介绍每条路径。

获取todos (/todos)

当获取todos ,我们正在发送一个GET 请求到/todos

为了记录这个路径,我们编辑/docs/todos/get-todos.js 文件,如下所示。

module.exports = {
  // method of operation
  get: {
    tags: ["Todo CRUD operations"], // operation's tag.
    description: "Get todos", // operation's desc.
    operationId: "getTodos", // unique operation id.
    parameters: [], // expected params.
    // expected responses
    responses: {
      // response code
      200: {
        description: "Todos were obtained", // response desc.
        content: {
          // content-type
          "application/json": {
            schema: {
              $ref: "#/components/schemas/Todo", // Todo model
            },
          },
        },
      },
    },
  },
};

从上面的代码片断中,我们已经。

  • 指定了操作的方法,get
  • 指定了操作的标签。
  • 用一个简短的description 和一个operationId 来描述这个操作。
  • 定义了parameters 。在这种情况下,没有定义。
  • 定义了预期的responses 。因为我们正在获取数据,所以只期望有一个200 响应。在响应中,我们还描述了内容类型,并将模式与使用$ref 的组件的模式相匹配。

为了测试这一点。

  • 确保开发服务器正在运行。
  • 在你的浏览器中,刷新文档页面。
  • 如果出现错误,请重新审视这些步骤。

否则,以下部分应该被添加到页面中。

get_todos_screenshot

获取todos路线的截图

要测试它,点击Try it out ,然后点击Execute ,然后检查服务器的响应。

获取单个todo (/todos/:id)

当获取单个todo时,我们要发送一个GET 请求到/todos/:id:id 是为动态id

为了记录这个路径,我们将编辑/docs/todos/get-todo.js 文件,如下所示。

module.exports = {
  // operation's method
  get: {
    tags: ["Todo CRUD operations"], // operation's tag.
    description: "Get a todo", // operation's desc.
    operationId: "getTodo", // unique operation id
    parameters: [
      // expected params.
      {
        name: "id", // name of the param
        in: "path", // location of the param
        schema: {
          $ref: "#/components/schemas/id", // data model of the param
        },
        required: true, // Mandatory param
        description: "A single todo id", // param desc.
      },
    ],
    // expected responses
    responses: {
      // response code
      200: {
        description: "Todo is obtained", // response desc.
        content: {
          // content-type
          "application/json": {
            schema: {
              $ref: "#/components/schemas/Todo", // todo data model
            },
          },
        },
      },
      // response code
      404: {
        description: "Todo is not found", // response desc.
        content: {
          // content-type
          "application/json": {
            schema: {
              $ref: "#/components/schemas/Error", // error data model
            },
          },
        },
      },
    },
  },
};

从上面来看,我们已经。

  • 指定了操作的方法,get
  • 定义了该操作的tags
  • 用一个简短的description 和一个operationId 来描述这个操作。
  • 指定了该操作的参数。

我们描述了参数的namein ,可以是path,header,query, 或cookie 。在我们的例子中,它是path 。我们使用$ref ,将参数指向其对应的模式组件,指定它是required ,并给出参数的简短description

  • 指定了预期的响应。对于这种情况,我们有两个。200404 。如果找到了todo,它将返回200 ,否则404 。对于每个响应,我们将给出一个简短的描述,阐述内容类型,并使用$ref 将模式与它的组件等价物相匹配。

为了测试这一点。

  • 确保开发服务器正在运行。
  • 在你的浏览器中,刷新文档页面。
  • 在出现错误的情况下,重温一下步骤。

以下部分应该被添加到页面中。

get_todo_screenshot

获取单一todo路线的截图

要试一下,从上一个操作中,得到一个id 。点击当前操作的Try it out 按钮,在参数部分粘贴id ,观察服务器的响应。

创建一个todo (/todos)

当创建一个todo时,我们要发送一个POST ,将todo的数据发送到/todos 。这些数据在requestBody

为了记录这个路径,我们需要编辑docs/todos/create-todo.js 文件,如下所示。

module.exports = {
  // operation's method
  post: {
    tags: ["Todo CRUD operations"], // operation's tag
    description: "Create todo", // short desc
    operationId: "createTodo", // unique operation id
    parameters: [], // expected params
    requestBody: {
      // expected request body
      content: {
        // content-type
        "application/json": {
          schema: {
            $ref: "#/components/schemas/TodoInput", // todo input data model
          },
        },
      },
    },
    // expected responses
    responses: {
      // response code
      201: {
        description: "Todo created successfully", // response desc
      },
      // response code
      500: {
        description: "Server error", // response desc
      },
    },
  },
};

从上面来看,我们已经。

  • 指定了操作的方法,post
  • 指定了操作的tags
  • 用一个简短的description 和一个operationId 来描述该操作。
  • 指定了操作的parameters 。对于这种情况,没有。
  • 指定了requestBody 。通过它,我们指定了内容类型,并使用$ref ,将模式与组件中的等价物相匹配。
  • 指定了预期的响应。如果todo被成功创建,201 ;如果有一个服务器错误,500

为了测试这一点。

  • 确保开发服务器正在运行。
  • 在你的浏览器中,刷新文档页面。
  • 在出现错误的情况下,重温一下步骤。

以下部分应该被添加到你的页面中。

create_todo_screenshot

创建todo路线的截图

为了测试功能,点击Try it out 按钮,在Request Body ,填写数据,点击Execute ,然后观察服务器的反应。

更新一个todo (/todos/:id)

当更新一个todo时,我们将发送一个PUT 请求到/todos/:id 。动态的id:id 。在这个API中,对todo的更新涉及到切换完成值。

为了记录这个路径,我们必须编辑docs/todos/update-todos.js 文件,如下所示。

module.exports = {
  // operation's method
  put: {
    tags: ["Todo CRUD operations"], // operation's tag
    description: "Update todo", // short desc
    operationId: "updateTodo", // unique operation id
    parameters: [
      // expected params
      {
        name: "id", // name of param
        in: "path", // location of param
        schema: {
          $ref: "#/components/schemas/id", // id model
        },
        required: true, // mandatory
        description: "Id of todo to be updated", // short desc.
      },
    ],
    // expected responses
    responses: {
      // response code
      200: {
        description: "Todo updated successfully", // response desc.
      },
      // response code
      404: {
        description: "Todo not found", // response desc.
      },
      // response code
      500: {
        description: "Server error", // response desc.
      },
    },
  },
};

从上面来看,我们已经。

  • 指定了操作的方法,put
  • 指定了操作的tags
  • 用一个简短的description 和一个operationId 来描述该操作。
  • 指定了操作的参数。我们已经描述了name,in,schema,required, 和description 属性。通过schema ,我们指出了使用$ref 的等价组件。
  • 指定了我们期望的不同响应。200 ,如果一个todo被成功更新,404 ,如果没有找到该id 的todo,以及500 ,如果有一个服务器错误更新todo。

要测试这个。

  • 确保开发服务器正在运行。
  • 在你的浏览器中,刷新文档页面。
  • 在出现错误的情况下,重温一下步骤。

以下部分应该被添加到你的页面中。

update_todo_screenshot

更新todo路线的截图

要尝试一下,从第一个操作中得到一个todo的id ,点击Try it out 按钮,在参数部分粘贴id ,点击Execute ,观察服务器的反应。

删除一个todo(/todos/:id)

当删除一个todo时,我们发送一个DELETE 请求到/todos/:id 。动态id:id

为了记录这个路径,我们必须编辑/docs/todos/delete-todo.js 文件,如下所示。

module.exports = {
  // operation's method.
  delete: {
    tags: ["Todo CRUD operations"], // operation's tag
    description: "Deleting a todo", // short desc
    operationId: "deleteTodo", // unique operation id
    parameters: [
      // expected parameters
      {
        name: "id", // name of param
        in: "path", // location of param
        schema: {
          $ref: "#/components/schemas/id", // id model
        },
        required: true, // mandatory
        description: "Deleting a done todo", // param desc
      },
    ],
    // expected responses
    responses: {
      // response code
      200: {
        description: "Todo deleted successfully", // response desc
      },
      // response code
      404: {
        description: "Todo not found", // response desc
      },
      // response code
      500: {
        description: "Server error", // response desc
      },
    },
  },
};

从上面来看,我们已经。

  • 指定了操作的方法,delete
  • 指定了操作的tags
  • 用简短的descriptionoperationId 来描述该操作。
  • 指定了parameters 。对于一个参数,我们描述了name,in,schema,required, 和description 的属性。
  • 指定了responses 。在这个操作中,如果todo被成功删除,我们可以得到一个200 响应,如果没有找到带有该id 的todo,则得到一个404 响应,如果删除todo时出现服务器错误,则得到一个500 响应。

要测试这一点。

  • 确保开发服务器正在运行。
  • 在你的浏览器中,刷新文档页面。
  • 如果出现错误,请重温一下步骤。

否则,应该在你的页面上添加以下部分。

delete_todo_screenshot

删除todo路线的截图

要尝试这个方法,获取更新的todo的id ,点击Try it out 按钮,在参数部分粘贴id ,点击Execute 按钮,然后观察服务器的反应。

结论

记录一个API是对该API可用性的一个优势,因为任何人都可以理解和使用它。

Swagger在记录RESTful API方面做了大量的工作。根据你正在构建的API,Swagger提供了大量的[功能]。