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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 23:48:12