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

异步获取Google Secret Manager凭据后如何初始化pg-promise

解决方案

方案1:使用顶级await(适配Node.js 14.18.0+ ES模块)

该方案改动最小,无需修改原有控制器的导入和调用逻辑,利用Node.js 14.3+支持的ES模块顶级await特性,等待密钥拉取完成后再初始化并导出pg-promise实例。
注意:使用该方案需要你的项目为ES模块规范,即package.json中配置"type": "module"

pgpProvider/index.ts 代码改造如下:

import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
import pgpromise, { IMain, IDatabase } from 'pg-promise';

// 保留原有pg-promise初始化配置
const initOptions = { /* 你的原有初始化参数 */ };
const pgp: IMain = pgpromise(initOptions);

// 从Google Secret Manager拉取数据库配置
async function fetchDbConfig() {
  const client = new SecretManagerServiceClient();
  // 替换为你的密钥资源路径
  const secretResource = 'projects/你的项目ID/secrets/你的数据库配置密钥名/versions/latest';
  const [version] = await client.accessSecretVersion({ name: secretResource });
  const secretData = version.payload?.data?.toString();
  if (!secretData) throw new Error('拉取数据库配置密钥失败');
  // 若密钥为JSON格式直接解析,若拆分存储多个密钥可在此处并行拉取后组装
  return JSON.parse(secretData) as {
    database: string;
    host: string;
    port: number;
    user: string;
    password: string;
  };
}

// 顶级await等待配置拉取完成后初始化数据库实例
const dbConfig = await fetchDbConfig();
const db: IDatabase<{}> = pgp(dbConfig);

export default db;

方案2:代理包装实例(兼容所有模块规范,无侵入改造)

如果你的项目使用CommonJS规范不支持顶级await,可以通过Proxy代理封装数据库实例,所有方法调用会自动等待初始化完成后执行,原有控制器代码完全无需修改。

pgpProvider/index.ts 代码改造如下:

import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
import pgpromise, { IMain, IDatabase } from 'pg-promise';

const initOptions = { /* 你的原有初始化参数 */ };
const pgp: IMain = pgpromise(initOptions);

let dbInstance: IDatabase<{}> | null = null;
// 初始化promise全局只执行一次
const dbInitPromise = (async () => {
  const client = new SecretManagerServiceClient();
  const [version] = await client.accessSecretVersion({ 
    name: 'projects/你的项目ID/secrets/你的数据库配置密钥名/versions/latest' 
  });
  const dbConfig = JSON.parse(version.payload!.data!.toString()!);
  dbInstance = pgp(dbConfig);
})();

// 导出代理对象,拦截所有方法调用
const db = new Proxy({} as IDatabase<{}>, {
  get(target, propKey) {
    return async (...args: unknown[]) => {
      // 等待数据库初始化完成
      await dbInitPromise;
      // 调用真实实例的对应方法
      return (dbInstance as any)[propKey](...args);
    };
  }
});

export default db;

配套优化建议

  • 若你将数据库用户名、密码等配置拆分存储为多个Secret Manager条目,可使用Promise.all并行拉取所有密钥,减少初始化耗时
  • 可在accessSecretVersion调用逻辑中增加3-5次的失败重试机制,避免临时网络波动导致服务启动失败
  • 服务启动时增加初始化校验,等数据库实例准备完成后再监听端口,避免启动初期请求报错,Express启动代码示例如下:
import express from 'express';
import db from './pgpProvider';

const app = express();
// 原有中间件、路由注册逻辑
// ...

// 等待数据库就绪后再监听端口
(async () => {
  // 简单测试数据库连接
  await db.any('SELECT 1');
  app.listen(3000, () => {
    console.log('服务启动成功,监听端口3000');
  });
})();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 15:54:08