Vercel环境下如何向cloud-sql-connector传自定义GoogleAuth凭证
问题:Vercel托管后端通过@google-cloud/cloud-sql-connector连接Cloud SQL的凭证配置问题
背景
本地开发时,设置GOOGLE_APPLICATION_CREDENTIALS环境变量指向服务账号凭证文件可正常连接Cloud SQL,但Vercel不支持文件路径,需通过环境变量传递自定义凭证替代默认配置。
已尝试方案及问题
直接传入JSON字符串到GoogleAuth的credentials字段
const connector = new Connector({ auth: new GoogleAuth({ credentials: process.env.CREDENTIALS_JSON, scopes: ['https://www.googleapis.com/auth/cloud-platform'], }) });报错:
Error: Could not load the default credentials本地测试使用keyFile路径
const connector = new Connector({ auth: new GoogleAuth({ keyFile: 'certificates/survey-engine-301112-d65599b32daa.json', scopes: ['https://www.googleapis.com/auth/cloud-platform'], }), });同样报错:
Error: Could not load the default credentials使用GoogleAuth.fromJSON解析环境变量中的JSON
代码无报错,但无法建立连接,当前代码如下:import mysql from 'mysql2/promise'; import {Connector} from '@google-cloud/cloud-sql-connector'; import {GoogleAuth} from 'google-auth-library'; let pool; // load the environment variable with our keys const keysEnvVar = process.env.CREDENTIALS_JSON if (!keysEnvVar) { throw new Error('The CREDENTIALS_JSON environment variable was not found!'); } const keys = JSON.parse(keysEnvVar); console.log("keys=",keys) const connector = new Connector({ auth: new GoogleAuth.fromJSON(keys), }); const clientOpts = await connector.getOptions({ instanceConnectionName: 'survey-engine-301112:australia-southeast2:database-australia', ipType: 'PUBLIC', scope: ['https://www.googleapis.com/auth/cloud-platform'], }); if (!pool){ pool = await mysql.createPool({ ...clientOpts, user: process.env.DBUSER, password: process.env.DBPASSWORD, }); } const conn = await pool.getConnection(); const [result] = await conn.query( `SELECT NOW();`); console.table(result); // prints returned time value from server conn.release(); export default pool已确认
keys解析正确(脱敏后输出):keys= { type: 'service_account', project_id: 'survey-######-30####', private_key_id: 'd65############e9f', private_key: '-----BEGIN PRIVATE KEY-----\n' + 'MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDkYpkZMXRJQfp+\n' + '#########################\n'+ 'cnTc7caSvsgRFpeLCMxV5REeQw==\n' + '-----END PRIVATE KEY-----\n', client_email: 'backend-to-db@survey-engine-301112.iam.gserviceaccount.com', client_id: '10#################22', auth_uri: 'https://accounts.google.com/o/oauth2/auth', token_uri: 'https://oauth2.googleapis.com/token', auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs', client_x509_cert_url: 'https://www.googleapis.com/robot/v1/metadata/x509/backend-to-db%40survey-engine-301112.iam.gserviceaccount.com', universe_domain: 'googleapis.com' }
依赖版本
- @google-cloud/cloud-sql-connector: 1.2.0
- google-auth-library: 9.2.0
- mysql2: 3.6.2
- Node.js: 20.10.0
解决方案
1. 修正GoogleAuth.fromJSON的调用方式
GoogleAuth.fromJSON是静态方法,不需要加new,直接调用即可返回有效GoogleAuth实例:
const auth = GoogleAuth.fromJSON(keys); const connector = new Connector({ auth });
2. 构造函数传参的正确方式
如果使用GoogleAuth构造函数,需传入解析后的JSON对象而非字符串到credentials字段:
const connector = new Connector({ auth: new GoogleAuth({ credentials: keys, // 传入解析后的对象,不是process.env.CREDENTIALS_JSON字符串 scopes: ['https://www.googleapis.com/auth/cloud-platform'], }) });
3. 调整connector.getOptions参数
scope是GoogleAuth的配置项,不属于getOptions的参数,需移除:
const clientOpts = await connector.getOptions({ instanceConnectionName: 'survey-engine-301112:australia-southeast2:database-australia', ipType: 'PUBLIC', });
4. 完整修正后的代码
import mysql from 'mysql2/promise'; import {Connector} from '@google-cloud/cloud-sql-connector'; import {GoogleAuth} from 'google-auth-library'; let pool; const keysEnvVar = process.env.CREDENTIALS_JSON; if (!keysEnvVar) { throw new Error('The CREDENTIALS_JSON environment variable was not found!'); } const keys = JSON.parse(keysEnvVar); // 方式一:使用fromJSON静态方法 const auth = GoogleAuth.fromJSON(keys); auth.scopes = ['https://www.googleapis.com/auth/cloud-platform']; const connector = new Connector({ auth }); // 方式二:使用构造函数传入解析后的credentials对象 // const connector = new Connector({ // auth: new GoogleAuth({ // credentials: keys, // scopes: ['https://www.googleapis.com/auth/cloud-platform'], // }) // }); const clientOpts = await connector.getOptions({ instanceConnectionName: 'survey-engine-301112:australia-southeast2:database-australia', ipType: 'PUBLIC', }); if (!pool) { pool = await mysql.createPool({ ...clientOpts, user: process.env.DBUSER, password: process.env.DBPASSWORD, database: process.env.DBNAME, // 建议添加目标数据库名称 }); } // 测试连接(生产环境可移除) try { const conn = await pool.getConnection(); const [result] = await conn.query(`SELECT NOW();`); console.table(result); conn.release(); } catch (err) { console.error('Connection test failed:', err); } export default pool;
5. Vercel环境变量配置注意事项
- 在Vercel控制台将服务账号JSON内容直接粘贴到
CREDENTIALS_JSON环境变量中,无需手动转义换行符; - 确保服务账号拥有
Cloud SQL Client角色权限,Cloud SQL实例的IAM配置正确。
内容的提问来源于stack exchange,提问作者Terence Sweeney
相关产品推荐
相关产品推荐

