如何构建支持按需安装适配器的模块化npm查询包?
我正在开发一款类查询工具的npm包easy-query,目标是支持MySQL、MSSQL等多种数据库并发布到npm仓库。目前的问题是必须预装所有数据库依赖(如mysql2、mssql、tedious、pgsql),造成了大量不必要的资源占用。
同时安装easy-query和easy-query-mysql后,主包无法导入适配器,推测是Node.js模块隔离机制导致。尝试动态导入easy-query-mysql时出现ERR_MODULE_NOT_FOUND错误,相关代码及目录结构如下:
app |- node_modules | |- easy-query | | |- index.js | |- easy-query-mysql |- index.js
/app/index.js
const query = await EasyQuery.connect('mysql://user:pass@localhost/database');
/app/node_modules/easy-query/index.js
const adapter = await import('easy-query-mysql')
需求:实现用户仅安装所需模块的功能——主包easy-query不含任何数据库适配器,用户安装easy-query及对应适配器(如npm i easy-query easy-query-mysql)后,主包可通过mysql://类连接字符串建立连接;安装easy-query-mssql则支持mssql://连接。
解决方案设计
1. 适配器包命名规范
统一适配器包命名为easy-query-[db-type],比如easy-query-mysql、easy-query-mssql,方便主包根据连接字符串前缀自动拼接包名。
2. 动态导入+错误捕获优化
在主包中解析连接字符串的数据库类型前缀,动态导入对应适配器,并捕获模块未找到的错误,给出明确的安装提示:
// easy-query/index.js export class EasyQuery { static async connect(connectionString) { const dbType = connectionString.split('://')[0]; const adapterPackage = `easy-query-${dbType}`; try { const adapter = await import(adapterPackage); return adapter.connect(connectionString); } catch (err) { if (err.code === 'ERR_MODULE_NOT_FOUND') { throw new Error(`请安装对应数据库适配器:npm install ${adapterPackage}`); } throw err; } } }
3. 适配器接口标准化
要求所有适配器包导出统一的接口,确保主包能无缝调用。例如必须包含connect方法,返回封装好的查询实例:
// easy-query-mysql/index.js import mysql2 from 'mysql2/promise'; export async function connect(connectionString) { const connection = await mysql2.createConnection(connectionString); return { query: async (sql, params) => connection.execute(sql, params), // 其他通用查询方法... }; }
4. 主包peerDependencies配置
在easy-query的package.json中配置可选的peer依赖,声明适配器的兼容版本范围但不强制安装:
{ "peerDependencies": { "easy-query-mysql": "^1.0.0", "easy-query-mssql": "^1.0.0" }, "peerDependenciesMeta": { "easy-query-mysql": { "optional": true }, "easy-query-mssql": { "optional": true } } }
这样npm会在用户安装时提示缺失的可选依赖,但不会强制安装所有适配器。
5. 独立包目录结构
主包和适配器包分开维护为独立npm包:
# 主包仓库 easy-query/ |- index.js |- package.json |- README.md # MySQL适配器仓库 easy-query-mysql/ |- index.js |- package.json |- README.md # 依赖mysql2
参考思路
可以参考现有成熟工具的实现模式:
- Sequelize:主包+对应数据库驱动(如
mysql2、pg),主包根据配置自动加载驱动 - Knex:采用主包+数据库驱动的分离模式,用户按需安装驱动
内容的提问来源于stack exchange,提问作者Magician

