Node.js and Cloud Functions
Node.js backends can connect to CloudBase PostgreSQL through the PostgreSQL protocol. This is suitable for complex SQL, transactions, batch jobs, background tasks, and external APIs.
Common CloudBase runtimes include Cloud Functions, CloudBase Run, and custom Node.js services. In production, store connection information in environment variables and reuse connection pools.
Install dependencies
npm install pg
For TypeScript, install type declarations as well.
npm install -D @types/pg
Configure environment variables
Store connection information in environment variables.
PGHOST=your-postgres-host
PGPORT=5432
PGDATABASE=postgres
PGUSER=app_user
PGPASSWORD=your-password
Do not commit database passwords to the repository. Configure environment variables in the Cloud Functions or CloudBase Run console.
Create a connection pool
Place the pool at module scope so Cloud Functions can reuse it across runtime invocations.
import pg from "pg";
const pool = new pg.Pool({
host: process.env.PGHOST,
port: Number(process.env.PGPORT || 5432),
database: process.env.PGDATABASE,
user: process.env.PGUSER,
password: process.env.PGPASSWORD,
ssl: { rejectUnauthorized: false },
max: Number(process.env.PGPOOL_MAX || 5),
idleTimeoutMillis: 30_000,
});
export async function query(sql, params = []) {
return pool.query(sql, params);
}
Cloud Function example
import { query } from "./db.js";
export async function main(event) {
const limit = Math.min(Number(event.limit || 20), 100);
const { rows } = await query(
"select id, title, done from public.todos order by created_at desc limit $1",
[limit]
);
return { data: rows };
}
Always pass user input through placeholders to prevent SQL injection.
Transactions
Use the same client for all statements in a transaction.
const client = await pool.connect();
try {
await client.query("begin");
await client.query("insert into public.orders(user_id, status) values($1, $2)", [userId, "pending"]);
await client.query("insert into public.logs(user_id, action) values($1, $2)", [userId, "create_order"]);
await client.query("commit");
} catch (error) {
await client.query("rollback");
throw error;
} finally {
client.release();
}
For more about transactions, see Transactions.
Connection pool recommendations
Serverless environments can start multiple instances at the same time. Estimate total connections as instance count times pool size to avoid exceeding the database limit.
Start with a small max, such as 3 to 5. For higher concurrency, combine slow query optimization, queue-based throttling, or connection pool proxy capabilities.
Permission boundary
Server-side direct connections usually use database accounts and do not automatically carry end-user identity. If server-side code needs user-scoped permissions, pass user_id explicitly in SQL or use restricted accounts and database policies.
Frontend users should access the database through RLS-protected paths. Do not expose server-side admin capabilities to clients.