likes
comments
collection
share

在NestJs中连接MongoDB如何快速生成Schema,别再全部手写了😁😁😁

作者站长头像
站长
· 阅读数 8

最近在开发一个后端项目,需要从另外一个后台语言转换成 NestJs 的,后端数据库使用的是 MongoDB。

背景

在使用的时候就遇到了一个问题,使用 MongoDB 会需要定义大量的 schema 配置才能做完成一个表的使用,并且类型也多,于是我想到了实现一个工具来对已有的返回数据对其进行初始化,然后生成一个初始的 schema,然后对其进行修改完善。

具体实现

假设我们有一个后台接口返回的数据是以下结构:

{
  "name": "string",
  "age": {
    "type": "number",
    "required": true,
    "min": 0
  },
  "gender": {
    "type": "enum",
    "values": ["male", "female", "non-binary", "prefer not to say"]
  },
  "address": {
    "street": "string",
    "city": "string",
    "country": "string"
  },
  "friends": [
    {
      "name": "string"
    }
  ]
}

数据我们有了,接下来我们创建一个node项目来编写相关的脚本,安装相关依赖:

npm i mongoose

之后在根目录下创建一个 index.js 文件并编写一下代码:

const fs = require("fs");

function generateSchemaClass(
  jsonSchema,
  className,
  generatedClasses = [],
  isSubClass = false
) {
  let tsCode = "";

  if (!isSubClass) {
    tsCode = `
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';
`;
  }

  tsCode += `
@Schema()
export class ${className} {
`;

  for (const [key, value] of Object.entries(jsonSchema)) {
    if (Array.isArray(value)) {
      const typeName = capitalize(key.slice(0, -1));

      if (!generatedClasses.includes(typeName)) {
        tsCode += generateSchemaClass(
          value[0],
          typeName,
          generatedClasses,
          true
        );
        generatedClasses.push(typeName);
      }
      jsonSchema[key] = [{ type: typeName }];
    }
  }

  for (const [key, value] of Object.entries(jsonSchema)) {
    if (typeof value === "string") {
      tsCode += `
  @Prop()
  ${key}: ${value};
`;
    } else if (value.type === "enum") {
      tsCode += `
  @Prop()
  ${key}: ${value.values.join(" | ")};
`;
    } else if (value.type) {
      tsCode += `
  @Prop(${value.required ? "{ required: true }" : ""})
  ${key}: ${value.type};
`;
    } else if (Array.isArray(value)) {
      tsCode += `
  @Prop([{ type: () => ${value[0].type} }])
  ${key}: ${value[0].type}[];
`;
    }
  }

  tsCode += `}
export const ${className.toLowerCase()}Schema = SchemaFactory.createForClass(${className});
export type ${className}Document = HydratedDocument<${className}>;
`;

  return tsCode;
}

function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

const jsonSchema = JSON.parse(fs.readFileSync("./index.json", "utf-8"));
const className = "Models";
const tsCode = generateSchemaClass(jsonSchema, className);
fs.writeFileSync("./path-to-output.ts", tsCode);

在上面的代码中,主要有以下步骤,如下所示

  1. 使用 Node.js 的 fs 模块用于文件的读取和写入。

  2. 创建一个主要用于生成 TypeScript 类定义的 generateSchemaClass 函数。

  3. 如果不是生成子类,则将 NestJS 和 Mongoose 的导入语句添加到生成的代码字符串中。

  4. 如果是子类,则生成一个带有 @Schema() 装饰器的类定义。

  5. 处理数组类型属性有以下步骤:

    • 对 JSON Schema 进行迭代,找出数组类型的属性。
    • 对每个数组类型的属性生成一个子类定义,并更新该属性的类型信息。
  6. 继续遍历 JSON Schema,并根据属性的类型添加相应的 @Prop 装饰器和类型定义。

  7. 将类的结束括号、SchemaFactory 创建语句和导出类型添加到代码字符串中。

  8. 实现一个 capitalize 函数用于将字符串首字母大写的辅助函数。

  9. 使用 fs 模块读取 JSON 文件,并将其内容解析为一个 JavaScript 对象。

  10. 调用 generateSchemaClass 函数,传入解析后的 JSON Schema 和目标类名,生成 TypeScript 代码。

  11. 将生成的 TypeScript 代码字符串写入到一个指定的 .ts 文件中。

通过递归生成主类和可能的子类定义,它能够处理嵌套的对象和数组类型属性。处理的结果是一个或多个装饰过的 TypeScript 类,可以在 NestJS 项目中用作 Mongoose 模型的定义。通过这种方式能够实现从数据模型到代码的自动转换,提高了开发效率并减少了手动编写模型定义时可能出现的错误。

总结

在日常开发的过程中,我们难免会遇到一些重复的工作,这个时候,我们就可以去想到实现一些工作来帮助我们去处理一下重复的工作,以提升工作效率了

最后分享两个我的两个开源项目,它们分别是:

这两个项目都会一直维护的,如果你也喜欢,欢迎 star 🚗🚗🚗