[前端利器]一键生成增删改查接口(基于Graphql)

313 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情 444.webp

简介

这是一个根据mysql表结构自动生成Graphql Schema的工具,其中包括常用的增删改查相关方法,配合graphql-server即可向前端提供增删改查相关接口,借助Graphql强大的接口组合功能,基本可以实现大部分的需求。

友情提示

此工具适用于对数据安全性要求不高的内部管理系统,对于单个接口需进行多表操作的,还是老老实实写接口吧。

此外,该工具生成的schema没有做鉴权,请不要用到对数据安全有要求的场景。

实现过程

实现原理

读取所有mysql表及其表结构,根据字段类型生成对应的Graphql Type,然后生成增删改查方法对应的schema及其resolver,其中resolver的原理就是拼接响应的sql语句并借助sequelize执行查询

1、获取所有表名

this.queryInterface = new Sequelize().getQueryInterface();
this.queryInterface.showAllTables().then(getAllTables,callback);

调用Sequelize的showAllTables方法获取所有表名,其原理是执行show tables;

示例:

sql.png

2、获取所有的表结构信息

image.png 调用Sequelize的describeTable方法获取表结构信息,其原理是执行desc [表名]

3、根据表结构和字段类型,生成对应的Graphql Type

schema的具体介绍请参考graphql官网 >>学习资料

这一步主要做的事情就是将mysql的数据类型映射到graphql的类型,对照关系如下:

mysql类型graphql类型
主键GraphQLID
INTGraphQLInt
TINYINTGraphQLInt
SMALLINTGraphQLInt
MEDIUMINTGraphQLInt
BIGINTGraphQLInt
FLOATGraphQLFloat
DOUBLEGraphQLFloat
其他类型GraphQLString

这里主要是为了省事,其他类型都当做字符串类型处理

转换结果如下图所示:

image.png

4、生成增删改查对应的的schema及其resolver

核心逻辑都在这里了,分为两步:

【构造schema】

给接口命名,指定返回值的类型,约束输入参数的名称及其类型

【构造resolver】

接口要实现的逻辑,这一步主要是拼接sql,并生成sequelize.query的执行代码

以按id查询单行记录为例,构造代码如下:

//findOne
text += 'const ' + resolverFindOne + ' = {\n'
text += indent + 'type: ' + typeName + ',\n'
text += indent + 'description: "findOneById",\n'
text += indent + 'args: {\n'
var sql = 'select * from ' + table + ' where 1=1'
var paramMap = '{'
_.each(primaryIds, function(id){
  text += indent + indent + id.key + ':{\n'
  text += indent + indent + indent + 'name :"' + id.key + '",\n'
  text += indent + indent + indent + 'type : new GraphQLNonNull(GraphQLID),\n'
  text += indent + indent + indent + 'description: "' + id.value + '"\n'
  text += indent + indent + '},\n'

  sql += ' and ' + id.key + '= :' + id.key
  paramMap += id.key + ': params.' + id.key + ','
})
paramMap += '}'
text += indent + '},\n'
text += indent + 'async resolve(obj, params, { sequelize }) {\n'
text += indent + indent + 'if (!sequelize) return null\n'
text += indent + indent + 'let list = await sequelize.query("' + sql + '", { type: sequelize.QueryTypes.SELECT, replacements: ' + paramMap + ' })\n'
text += indent + indent + 'return list[0]\n'
text += indent + '}\n'
text += '}\n\n'

生成的代码如下:

const getUserById = {
  type: userType,
  description: "findOneById",
  args: {
    id:{
      name :"id",
      type : new GraphQLNonNull(GraphQLID),
      description: ""
    },
  },
  async resolve(obj, params, { sequelize }) {
    if (!sequelize) return null
    let list = await sequelize.query("select * from t_user where 1=1 and id= :id", { type: sequelize.QueryTypes.SELECT, replacements: {id: params.id,} })
    return list[0]
  }
}

5、保存到文件

将生成的代码保存到本地文件,每张表对应一个文件。

AutoSchema.prototype.write = function(data, callback) {
  var tables = _.keys(data)
  var self = this

  mkdirp.sync(path.resolve(self.options.outpath));

  asyncUtil.each(tables, outputFile, callback)

  function outputFile(table, _callback) {
    var fileName = self.options.camelCaseForFileName ? _.camelCase(table) : table;
    fs.writeFile(path.resolve(path.join(self.options.outpath, fileName + '.js')), data[table], _callback);
  }
}

graphql调试

【Query】

graphql_user_query.png

【Mutation】

graphql_user_mutation.png

完整代码及示例

示例表结t_user.sql

生成代码文件t_user.js

github完整代码

总结

在做一些内部管理系统,或者小工具之类的,设计好表结构即表示完成了相应接口的开发,用起来的还是很方便的;特别是对于前端来说,一个人就可以搞定的事情,就不要依赖其他人了;

这是一个不错的偷懒工具,与其浪费时间去写那些没有技术含量又耗费时间的接口,还不如花点儿时间出去happy或者充充电,毕竟开心最重要,哈哈哈~~~

欢迎有需要的朋友收藏,如有描述的不对或者需要改进的地方,欢迎大家帮忙指正,感谢! github传送门