NodeJS Lambda如何在MySQL查询回调中正确返回API Gateway响应?
问题描述
我有一个通过API Gateway HTTP调用触发的Node.js Lambda函数,使用mysql库查询数据并将其作为结果返回。但无法将查询结果作为调用响应返回,总是返回对象的初始空值。示例代码如下:
import { createRequire } from 'module'; const require = createRequire(import.meta.url); const mysql = require('mysql'); const con = mysql.createConnection({ host: process.env.RDS_HOSTNAME, user: process.env.RDS_USERNAME, password: process.env.RDS_PASSWORD, port: process.env.RDS_PORT, database: process.env.RDS_DATABASE, }); const tableName = 'fit_guide'; export const handler = async (event, context, callback) => { let response = {}; const routeKey = event.routeKey; let sql = `SELECT * FROM ${tableName}`; con.query(sql, function (error, results, fields) { if (error) throw error; // 此处可以打印数据库表的查询结果 console.log('******** ', results); // 此处无法返回结果 response = { statusCode: 200, success: true, results, }; callback(null, JSON.stringify(response)); return response; }); con.end(); // 此处返回的是初始值空对象 {} callback(null, JSON.stringify(response)); return response; };
问题原因
- 异步回调时机问题:
con.query是异步回调模式,Lambda的handler函数在执行到callback(null, JSON.stringify(response))时,数据库查询还未完成,因此返回的是初始空对象{}。 - 重复调用callback:代码中调用了两次
callback,这会导致Lambda提前返回初始值,后续查询完成后的callback调用无效。 - 连接提前关闭:
con.end()放在查询回调外部,会导致数据库连接在查询完成前就被关闭,可能导致查询失败或结果丢失。
解决方案
方案1:将mysql回调包装为Promise(基于原mysql库)
通过Promise包装异步查询操作,利用async/await等待查询完成后再返回结果,同时调整连接生命周期:
import { createRequire } from 'module'; const require = createRequire(import.meta.url); const mysql = require('mysql'); const tableName = 'fit_guide'; export const handler = async (event) => { // 每次请求创建新连接,避免容器复用导致的连接失效 const con = mysql.createConnection({ host: process.env.RDS_HOSTNAME, user: process.env.RDS_USERNAME, password: process.env.RDS_PASSWORD, port: process.env.RDS_PORT, database: process.env.RDS_DATABASE, }); try { // 将query包装为Promise,等待查询完成 const results = await new Promise((resolve, reject) => { const sql = `SELECT * FROM ${tableName}`; con.query(sql, (error, queryResults) => { if (error) reject(error); else resolve(queryResults); }); }); // 返回查询结果,API Gateway需要正确的Content-Type头 return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ success: true, results }) }; } catch (error) { // 捕获并返回错误信息 console.error('数据库查询失败:', error); return { statusCode: 500, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ success: false, error: error.message }) }; } finally { // 无论成功失败,确保关闭连接 con.end(); } };
方案2:改用mysql2/promise(原生支持Promise)
使用mysql2/promise库,它原生支持Promise和async/await,代码更简洁:
import { createRequire } from 'module'; const require = createRequire(import.meta.url); // 使用mysql2的Promise版本 const mysql = require('mysql2/promise'); const tableName = 'fit_guide'; export const handler = async (event) => { let connection; try { // 创建连接并等待完成 connection = await mysql.createConnection({ host: process.env.RDS_HOSTNAME, user: process.env.RDS_USERNAME, password: process.env.RDS_PASSWORD, port: process.env.RDS_PORT, database: process.env.RDS_DATABASE, }); // 直接await查询,解构获取结果 const [results] = await connection.execute(`SELECT * FROM ${tableName}`); return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ success: true, results }) }; } catch (error) { console.error('数据库查询失败:', error); return { statusCode: 500, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ success: false, error: error.message }) }; } finally { // 确保连接关闭 if (connection) await connection.end(); } };
关键改动说明
- 异步处理:通过Promise或原生Promise库,让Lambda等待数据库查询完成后再返回结果。
- 连接生命周期:每次请求创建新连接,避免Lambda容器复用导致的连接失效问题;用
finally确保连接无论成功失败都会关闭。 - 响应格式:添加
Content-Type: application/json响应头,确保API Gateway能正确解析返回内容;直接返回响应对象,不再使用callback(async handler会自动处理返回值)。
内容的提问来源于stack exchange,提问作者Multitut
相关产品推荐
相关产品推荐

