You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js/Express中MySQL2 Promise API的正确连接方式咨询

MySQL2 Promise API 数据库连接正确方案

使用MySQL2 Promise API时,核心问题是mysql.createConnection()返回的是Promise对象,而非直接的数据库连接实例。如果直接将其赋值给变量,拿到的是Promise而非可用的连接,必须通过await或.then()链式调用获取实际连接实例。

方案1:转为ES模块,使用顶层await

将项目改为ES模块(在package.json中添加"type": "module"),即可直接在顶层使用await获取连接。

import dotenv from 'dotenv'
dotenv.config()

import mysql from 'mysql2/promise.js'
import express from 'express'
import ejs from 'ejs'
import {faker} from '@faker-js/faker'
import path from 'path'
const app = express();

app.set("view engine", "ejs");
app.use(express.json())

// mysql2/promise的createConnection已自动建立连接,无需额外调用connect()
const connection = await mysql.createConnection({
    host: "localhost",
    user: "root",
    password: process.env.SQL_PASSWORD,
    database: "joinus"
})

app.get('/users', async(req,res,next) => {
    // query返回[结果集, 字段信息],通常取第一个元素即可
    const [rows] = await connection.query('SELECT * FROM users')
    res.send(rows)
})

app.listen(5000, () => {
    console.log("服务启动在5000端口");
});

说明:

  • 优点:代码简洁,无需额外包裹函数
  • 注意:需将项目设为ES模块(package.json配置"type": "module"),Node.js版本需支持顶层await(v14.8+)

方案2:用async主函数包裹应用逻辑

无需修改模块类型,将所有依赖数据库的逻辑放在async函数中,调用该函数启动应用。

if (process.env.NODE_ENV !== "production") {
    require("dotenv").config();
}
const mysql = require("mysql2/promise");
const express = require("express");
const ejs = require("ejs");
const { faker } = require('@faker-js/faker');
const path = require("path");
const app = express();

app.set("view engine", "ejs");
app.set("views", path.join(__dirname + "/views"));
app.use(express.json())

async function main() {
    const connection = await mysql.createConnection({
        host: "localhost",
        user: "root",
        password: process.env.SQL_PASSWORD,
        database: "joinus"
    })

    app.get('/users', async(req,res,next) => {
        const [rows] = await connection.query('SELECT * FROM users')
        res.send(rows)
    })

    app.listen(5000, () => {
        console.log("服务启动在5000端口");
    });
}
// 记得捕获启动过程中的错误,避免未处理的Promise异常
main().catch(err => console.error("应用启动失败:", err))

说明:

  • 优点:兼容CommonJS模块(无需修改package.json),逻辑清晰易读
  • 注意:必须调用main()并捕获错误,避免程序崩溃

方案3:使用.then()链式调用处理连接

通过Promise的.then()方法获取连接实例,赋值给变量后使用。

if (process.env.NODE_ENV !== "production") {
    require("dotenv").config();
}
const mysql = require("mysql2/promise");
const express = require("express");
const ejs = require("ejs");
const { faker } = require("@faker-js/faker");
const path = require("path");
const app = express();

app.set("view engine", "ejs");
app.set("views", path.join(__dirname + "/views"));
app.use(express.json());

let connection;
mysql.createConnection({
    host: "localhost",
    user: "root",
    password: process.env.SQL_PASSWORD,
    database: "joinus"
}).then((result) => {
    connection = result;
    console.log("数据库连接成功");
}).catch(err => console.error("数据库连接失败:", err))

app.get("/users", async (req, res, next) => {
    // 需额外判断连接是否就绪,否则会出现未定义错误
    if (!connection) {
        return res.status(503).send("数据库连接未就绪");
    }
    const [rows] = await connection.query("SELECT * FROM users");
    res.send(rows);
});

app.listen(5000, () => {
    console.log("服务启动在5000端口");
});

说明:

  • 缺点:存在连接未就绪时请求先到达的风险,代码可读性不如async/await
  • 适合:习惯Promise链式调用写法的场景

方案4:使用连接池(推荐生产环境)

单连接无法应对高并发场景,mysql2的连接池会自动管理连接的创建、复用与释放,是生产环境的标准实践。

if (process.env.NODE_ENV !== "production") {
    require("dotenv").config();
}
const mysql = require("mysql2/promise");
const express = require("express");
const ejs = require("ejs");
const { faker } = require('@faker-js/faker');
const path = require("path");
const app = express();

app.set("view engine", "ejs");
app.set("views", path.join(__dirname + "/views"));
app.use(express.json())

// 创建连接池,配置最大连接数等参数
const pool = mysql.createPool({
    host: "localhost",
    user: "root",
    password: process.env.SQL_PASSWORD,
    database: "joinus",
    waitForConnections: true,
    connectionLimit: 10, // 最大并发连接数
    queueLimit: 0
})

app.get('/users', async(req,res,next) => {
    // 直接通过连接池执行查询,无需手动管理连接
    const [rows] = await pool.query('SELECT * FROM users')
    res.send(rows)
})

app.listen(5000, () => {
    console.log("服务启动在5000端口");
});

说明:

  • 优点:自动复用连接,提升并发性能,无需手动处理连接关闭
  • 推荐:生产环境和有并发需求的应用优先使用

新手开发者推荐方向

  1. 刚接触Node.js的新手优先选方案2,无需修改模块配置,逻辑清晰,还能熟练掌握async/await的正确用法。
  2. 熟悉ES模块的开发者可以用方案1,写法更简洁。
  3. 尽量避免方案3,存在连接未就绪的风险,代码维护性差。
  4. 当应用需要处理并发请求时,直接采用方案4(连接池),这是生产环境的最优选择。

内容的提问来源于stack exchange,提问作者northendben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:07:24