持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
简介
这是一个根据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;
示例:
2、获取所有的表结构信息
调用Sequelize的describeTable方法获取表结构信息,其原理是执行desc [表名]
3、根据表结构和字段类型,生成对应的Graphql Type
schema的具体介绍请参考graphql官网 >>学习资料
这一步主要做的事情就是将mysql的数据类型映射到graphql的类型,对照关系如下:
mysql类型 | graphql类型 |
---|---|
主键 | GraphQLID |
INT | GraphQLInt |
TINYINT | GraphQLInt |
SMALLINT | GraphQLInt |
MEDIUMINT | GraphQLInt |
BIGINT | GraphQLInt |
FLOAT | GraphQLFloat |
DOUBLE | GraphQLFloat |
其他类型 | GraphQLString |
这里主要是为了省事,其他类型都当做字符串类型处理
转换结果如下图所示:
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】
【Mutation】
完整代码及示例
总结
在做一些内部管理系统,或者小工具之类的,设计好表结构即表示完成了相应接口的开发,用起来的还是很方便的;特别是对于前端来说,一个人就可以搞定的事情,就不要依赖其他人了;
这是一个不错的偷懒工具,与其浪费时间去写那些没有技术含量又耗费时间的接口,还不如花点儿时间出去happy或者充充电,毕竟开心最重要,哈哈哈~~~
欢迎有需要的朋友收藏,如有描述的不对或者需要改进的地方,欢迎大家帮忙指正,感谢! github传送门