最近在开发一个后端项目,需要从另外一个后台语言转换成 NestJs 的,后端数据库使用的是 MongoDB。
背景
在使用的时候就遇到了一个问题,使用 MongoDB 会需要定义大量的 schema 配置才能做完成一个表的使用,并且类型也多,于是我想到了实现一个工具来对已有的返回数据对其进行初始化,然后生成一个初始的 schema,然后对其进行修改完善。
具体实现
假设我们有一个后台接口返回的数据是以下结构:
json
{
"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项目来编写相关的脚本,安装相关依赖:
bash
npm i mongoose
之后在根目录下创建一个 index.js
文件并编写一下代码:
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);
在上面的代码中,主要有以下步骤,如下所示
-
使用 Node.js 的
fs
模块用于文件的读取和写入。 -
创建一个主要用于生成 TypeScript 类定义的
generateSchemaClass
函数。 -
如果不是生成子类,则将 NestJS 和 Mongoose 的导入语句添加到生成的代码字符串中。
-
如果是子类,则生成一个带有
@Schema()
装饰器的类定义。 -
处理数组类型属性有以下步骤:
- 对 JSON Schema 进行迭代,找出数组类型的属性。
- 对每个数组类型的属性生成一个子类定义,并更新该属性的类型信息。
-
继续遍历 JSON Schema,并根据属性的类型添加相应的
@Prop
装饰器和类型定义。 -
将类的结束括号、
SchemaFactory
创建语句和导出类型添加到代码字符串中。 -
实现一个
capitalize
函数用于将字符串首字母大写的辅助函数。 -
使用
fs
模块读取 JSON 文件,并将其内容解析为一个 JavaScript 对象。 -
调用
generateSchemaClass
函数,传入解析后的 JSON Schema 和目标类名,生成 TypeScript 代码。 -
将生成的 TypeScript 代码字符串写入到一个指定的
.ts
文件中。
通过递归生成主类和可能的子类定义,它能够处理嵌套的对象和数组类型属性。处理的结果是一个或多个装饰过的 TypeScript 类,可以在 NestJS 项目中用作 Mongoose 模型的定义。通过这种方式能够实现从数据模型到代码的自动转换,提高了开发效率并减少了手动编写模型定义时可能出现的错误。
总结
在日常开发的过程中,我们难免会遇到一些重复的工作,这个时候,我们就可以去想到实现一些工作来帮助我们去处理一下重复的工作,以提升工作效率了
最后分享两个我的两个开源项目,它们分别是:
这两个项目都会一直维护的,如果你也喜欢,欢迎 star 🚗🚗🚗