为什么Node.js项目里要用代码建库
很多初学者拿到一个Node.js项目后,第一反应是打开Navicat或者命令行,手动敲一句CREATEDATABASE,然后再回到代码里写业务逻辑。这个做法本身没有错,但在真实项目里会带来一个很实际的问题:环境不一致。
开发机上手动建的库叫studentdb,测试环境同事建成了student_db,到了服务器上又忘了建。等代码跑起来报ER_BAD_DB_ERROR的时候,排查成本就上去了。把建库这一步放进Node.js代码里,本质上是把数据库初始化变成可重复执行、可版本控制的动作,这也是后续做自动化部署和CI流程的基础。
Node.js本身不带MySQL驱动,官方生态里用得比较多的是mysql2。它在mysql包的基础上做了Promise封装和预处理语句支持,性能表现也更适合现在项目。下面从环境准备开始,一步步走完整个流程。
环境准备与mysql2安装
动手之前确认三件事:Node.js已安装、MySQLServer正在运行、有一个可用的MySQL账号。账号权限这块后面会单独说,先记着CREATEDATABASE需要对应的权限。
新建项目目录并初始化:
mkdir node-mysql
cd node-mysql
npm init -y
安装mysql2:
npm install mysql2
npminit-y会生成一个默认的package.json,省去一路回车的麻烦。安装完成后,mysql2会出现在dependencies里。这里建议把它放在dependencies而不是devDependencies,因为生产环境运行时同样需要它。
建立MySQL连接:createConnection的配置细节
在项目根目录新建app.js,先写连接部分:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password'
});
connection.connect((err) => {
if (err) {
console.error('连接 MySQL 失败:', err.message);
return;
}
console.log('已连接到 MySQL 服务器');
});
createConnection()接收一个配置对象,host、user、password是三个基本项。这里刻意没有写database字段,原因很直接:库还没建出来,指定一个不存在的库名,连接阶段就会直接报错。
实际项目里,这些配置不建议硬编码在代码中。用process.env读取环境变量是更稳妥的做法,比如host:process.env.DB_HOST,配合.env文件管理不同环境的差异。硬编码的密码一旦提交到Git仓库,后续处理起来会很麻烦。
connection.connect()的回调里做了错误判断,err.message会给出具体原因。常见的ECONNREFUSED通常意味着MySQL服务没启动,或者host、端口配置不对。
执行CREATEDATABASE:把SQL交给connection.query
连接成功之后,建库这一步就是把SQL语句通过connection.query()发出去:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password'
});
connection.connect((err) => {
if (err) {
console.error('连接 MySQL 失败:', err.message);
return;
}
const sql = 'CREATE DATABASE studentdb';
connection.query(sql, (err) => {
if (err) {
console.error('创建数据库失败:', err.message);
return;
}
console.log('studentdb 数据库创建成功');
connection.end();
});
});
运行nodeapp.js,终端会依次输出连接成功和建库成功的信息。
这段代码里有几个点值得留意。connection.query()是异步的,建库结果在回调里处理;connection.end()放在建库成功之后调用,作用是关闭连接、释放资源。如果忘了end(),Node.js进程可能不会自动退出,命令行会一直挂在那里。
CREATEDATABASEstudentdb这条语句本身很直白:CREATEDATABASE是命令,studentdb是库名。执行成功后,MySQL服务器上就多了一个名为studentdb的数据库。
用IF NOT EXISTS避免重复建库报错
上面的代码跑第二遍就会出问题。库已经存在,MySQL会抛ER_DB_CREATE_EXISTS错误。脚本类操作经常需要重复执行,每次都手动判断库存不存在并不现实。
IF NOT EXISTS就是为这个场景准备的:
const sql = 'CREATE DATABASE IF NOT EXISTS studentdb';
connection.query(sql, (err) => {
if (err) {
console.error('操作失败:', err.message);
return;
}
console.log('数据库已就绪');
});
加上这个子句后,库不存在就创建,已存在就跳过,不会报错。项目初始化脚本、Docker启动脚本里用这种写法比较常见。它只是不报错,并不会告诉你“这次到底建了没有”。如果业务上需要区分这两种情况,得配合SHOWDATABASES之类的查询单独判断。
验证建库结果
建完之后确认一下比较稳妥。可以在MySQL客户端里执行:
SHOW DATABASES;
输出里应该能看到studentdb,同时还有information_schema、mysql、performance_schema、sys这几个系统库。系统库不要动,它们是MySQL自身运行需要的。
如果习惯在Node.js里验证,也可以把SHOWDATABASES通过connection.query()查出来打印,不过对于建库这种一次性操作,直接用客户端确认更省事。
错误处理:几个高频报错及排查方向
建库过程中出错的概率不低,把常见错误认全,排查会快很多。
| 错误信息 | 常见原因 |
|---|---|
Accessdeniedforuser |
用户名或密码不对 |
ER_DB_CREATE_EXISTS |
数据库已存在,未加IF NOT EXISTS |
ECONNREFUSED |
MySQL服务未启动或地址不可达 |
ER_DBACCESS_DENIED_ERROR |
当前账号没有建库权限 |
err.message会带上具体描述,先看这一行,基本能定位到大方向。权限类问题需要在MySQL里给账号授权,比如GRANTCREATEON*.*TO'user'@'localhost';,具体授权范围按项目需要来,不建议直接给全库全权限。
本节课程知识要点
-
Node.js通过
mysql2包与MySQL通信,需先npminstallmysql2。 -
createConnection()配置中不写database,因为建库阶段目标库尚不存在。 -
connection.query()负责把SQL语句发送到MySQL服务器。 -
CREATEDATABASEIF NOT EXISTS用于避免重复建库报错,适合脚本化场景。 -
操作完成后调用
connection.end()释放连接。 -
err.message是排查连接与权限问题的第一手信息。 -
MySQL账号需要具备
CREATE权限才能建库。
练习与思考
-
把
host、user、password改成从环境变量读取,写一个.env示例文件。 -
用
mysql2/promise改写建库代码,体验Promise写法与回调写法的差异。 -
如果要在建库之后立刻建一张表,代码结构应该怎么调整?提示:需要先让连接指向新建的库。
现在框架中的惯用写法
在Express、NestJS这类框架里,数据库连接通常不会在每个文件里单独createConnection。更常见的做法是建一个连接池模块,导出pool实例,业务层通过pool执行查询。mysql2提供createPool(),用法和createConnection()接近,但更适合处理并发请求。建库这种初始化动作,一般会放在项目启动脚本或迁移工具里,而不是混在业务代码中执行。
用Node.js建MySQL数据库这件事,代码量不大,但涉及连接配置、SQL执行、错误处理和权限管理几个环节。把IF NOT EXISTS和connection.end()这两个细节处理好,脚本的健壮性会有明显提升。实际项目里再往前走一步,就是把这套逻辑封装成可复用的初始化模块,配合环境变量和迁移工具,让数据库结构管理变得可控。