cloudsqlpostgres SQL dialect (Google Cloud SQL for PostgreSQL).cloudsqlpostgrescloudsqlpostgrespg driver, port 5432 and the same SQL. SoCloudSQLPostgresDialect extends PostgresDialect and inheritscloudsqlsuperuser cannot run.| Property | Value |
|---|---|
| Dialect name | cloudsqlpostgres |
| Extends | PostgresDialect |
| Driver | pg |
| Optional SDK | @google-cloud/cloud-sql-connector, google-auth-library |
| Default port | 5432 |
| Cloud SDK | @google-cloud/cloud-sql-connector, google-auth-library (connector / IAM only) |
| Local runtime | none — hosted service only |
connect() first runs in connector mode, and google-auth-library when$ npm install ts-prorm-orm pg$ pnpm add ts-prorm-orm pg$ yarn add ts-prorm-orm pg$ bun add ts-prorm-orm pgpg is the same driver the postgres dialect uses. Add @google-cloud/cloud-sql-connector and google-auth-library only for the token-based logins under Managed and cloud variants. pg-copy-streams is optional: only the features that need it load it.import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'cloudsqlpostgres',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: { instanceConnectionName: 'my-project:us-central1:my-instance' },
},
});import { DataTypes } from 'ts-prorm-orm';
const User = db.define('User', {
id: { type: DataTypes.INTEGER(), primaryKey: true, autoIncrement: true },
email: { type: DataTypes.STRING(255), allowNull: false, unique: true },
name: { type: DataTypes.STRING(100) },
meta: { type: DataTypes.JSONB() },
});
await db.sync(); // CREATE TABLE — see Generated SQL belowmeta is JSONB, which the containment query below needs.import { Op } from 'ts-prorm-orm';
await User.create({ email: 'ada@example.com', name: 'Ada' });
const page = await User.findAll({
where: { name: { [Op.like]: 'A%' } },
order: [['id', 'ASC']],
limit: 20,
offset: 40,
});import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'cloudsqlpostgres',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: {
instanceConnectionName: 'my-project:us-central1:my-instance',
ipType: 'PRIVATE', // 'PUBLIC' (default) | 'PRIVATE' | 'PSC'
},
},
});@google-cloud/cloud-sql-connector installed next to the driver.import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'cloudsqlpostgres',
database: 'app',
username: 'app-sa@my-project.iam', // the IAM principal; no password
dialectOptions: {
cloudSql: {
instanceConnectionName: 'my-project:us-central1:my-instance',
iam: true, // automatic IAM database authentication
},
},
});@google-cloud/cloud-sql-connector installed next to the driver.import { Prorm } from 'ts-prorm-orm';
import * as fs from 'fs';
export const db = new Prorm({
dialect: 'cloudsqlpostgres',
host: '10.0.0.5',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: {
mode: 'direct',
instanceConnectionName: 'my-project:us-central1:my-instance',
serverCa: fs.readFileSync('server-ca.pem', 'utf8'),
},
},
});query(), captured with the driver stubbed out. The model layer inlines the values.db.sync() for the model in the Quick start:CREATE TABLEawait User.create({ email: 'ada@example.com', name: 'Ada' });
await User.update({ name: 'Grace' }, { where: { email: 'ada@example.com' } });
await User.destroy({ where: { id: 1 } });await User.upsert({ id: 1, email: 'ada@example.com', name: 'Ada Lovelace' });upsert() also reads the row by primary key before and after the write, to return the instance.await User.findAll({
where: { name: { [Op.like]: 'A%' } },
order: [['id', 'ASC']],
limit: 20,
offset: 40,
});meta column:import { Op } from 'ts-prorm-orm';
await User.findAll({ where: { meta: { [Op.contains]: { plan: 'pro' } } } });await db.transaction(async (t) => {
await User.create({ email: 'grace@example.com', name: 'Grace' }, { transaction: t });
// A nested transaction is a SAVEPOINT; its failure undoes only its own work.
await db
.transaction(async (sp) => {
await User.destroy({ where: { id: 1 }, transaction: sp });
throw new Error('keep the row');
}, { transaction: t })
.catch(() => {});
});SKIP LOCKED)lock with skipLocked inside a transaction:await db.transaction(async (t) => {
await User.findAll({ where: { name: 'Ada' }, lock: { level: 'UPDATE', skipLocked: true }, transaction: t });
});STRING → VARCHAR(255), auto-increment INTEGER → SERIAL, BOOLEAN → BOOLEAN, DATE → TIMESTAMP, UUID → UUID, JSON → JSON. The type comparison lines the major engines up side by side.dialectOptions.cloudSql (shared by thesrc/dialects/cloud-sql-shared.ts).const db = new Prorm({
dialect: 'cloudsqlpostgres',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: {
instanceConnectionName: 'my-project:us-central1:my-instance',
ipType: 'PRIVATE', // 'PUBLIC' (default) | 'PRIVATE' | 'PSC'
},
},
});@google-cloud/cloud-sql-connector opens an mTLS tunnel to the instance, soconnect().stream factory. The connector returns a stream factory, and theLISTEN/NOTIFY client.stream() opens its own tunnelssl. The dialect removes ssl, because the tunnel is already TLS.disconnect(). The connector is also closed when the firstconnect() creates a new one.cloudSql.connector: new Connector() to use yourimport * as fs from 'fs';
const db = new Prorm({
dialect: 'cloudsqlpostgres',
host: '10.0.0.5',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: {
serverCa: fs.readFileSync('server-ca.pem', 'utf8'),
// per-instance CA: name the instance so its certificate can be checked
mode: 'direct',
instanceConnectionName: 'my-project:us-central1:my-instance',
// or, shared / customer-managed CA: the DNS name in the certificate's SAN
// serverName: '1a23b4cd5e67.1a2b345c6d27.us-central1.sql.goog',
// clientCert, clientKey: for ssl_mode TRUSTED_CLIENT_CERTIFICATE_REQUIRED
},
},
});gcloud sql instances describe INSTANCE --format='value(serverCaCert.cert)'.gcloud sql ssl server-ca-certs list --format='value(cert)' --instance=INSTANCE.serverCa, the dialect sets ssl to { rejectUnauthorized: true, ca }| Server CA mode | Certificate names | Check |
|---|---|---|
| Shared CA / customer-managed CA | the instance DNS name (SAN) | serverName: standard hostname check against that name (servername is sent too), even if host is an IP |
Per-instance CA (GOOGLE_MANAGED_INTERNAL_CA) | the instance as project:instance (CN), no host | instanceConnectionName with mode: 'direct': CN must equal project:instance; the chain is verified against a CA that signs only this instance |
| neither given | Node's default: the certificate must name host |
serverCa, your own ssl option is used unchanged.cloudsql.iam_authentication and add.gserviceaccount.com. SetcloudSql.iamPrincipal and the dialect builds it (googleIamDatabaseUser).username is not set.cloudSql.iam | What happens |
|---|---|
'automatic' | The connector logs in inside its tunnel (authType: 'IAM'). No password is sent. Needs the connector. |
'manual' | An OAuth2 access token (scope https://www.googleapis.com/auth/sqlservice.login) is the password. pg calls password() for every new connection, so each connection gets a current token (pgAuth). Default provider: googleIamTokenProvider({ target: 'cloud-sql', ...cloudSql.googleAuth }), which uses ADC, or keyFilename / credentials / authClient. Or pass cloudSql.tokenProvider. |
true | 'automatic' with the connector, 'manual' when direct |
const db = new Prorm({
dialect: 'cloudsqlpostgres',
database: 'app',
dialectOptions: {
cloudSql: {
instanceConnectionName: 'p:us-central1:i',
iam: true,
iamPrincipal: 'app@p.iam.gserviceaccount.com', // → user 'app@p.iam'
},
},
});serverCa, or the connector.cloudsqlsuperuser,postgres user has neither SUPERUSER nor REPLICATION. Serverquery() checks every statement before sending it. The same check coverscreateExtension() and createUser()/createRole(). It ignores text insideCloudSqlUnsupportedError (code: 'CLOUDSQL_UNSUPPORTED'):| Statement | Instead |
|---|---|
ALTER SYSTEM ... | Database flags: gcloud sql instances patch INSTANCE --database-flags=NAME=VALUE, or the console |
CREATE/ALTER ROLE/USER ... SUPERUSER | GRANT cloudsqlsuperuser TO <role>, or gcloud sql users create / the console |
CREATE EXTENSION / CREATE LANGUAGE for plpython3u, plpython2u, plpythonu, plperlu, pltclu | plpgsql or plv8 |
gcloud sql instances patch --database-flags replaces the instance's wholeCREATE EXTENSION. IfCREATE EXTENSION fails for one of them, the dialect adds the flag's name toCLOUDSQL_POSTGRES_EXTENSION_FLAGS.| Extension | Flag |
|---|---|
pgaudit | cloudsql.enable_pgaudit |
pglogical | cloudsql.enable_pglogical |
pg_cron | cloudsql.enable_pg_cron |
pg_hint_plan | cloudsql.enable_pg_hint_plan |
pg_squeeze | cloudsql.enable_pg_squeeze |
pg_bigm | cloudsql.enable_pg_bigm |
pg_wait_sampling | cloudsql.enable_pg_wait_sampling |
anon | cloudsql.enable_anon |
cloudsqlsuperuser can still do the following, so none of it is refused:CREATE CAST / DROP CAST;SET.google_ml_integration (embedding()), which you can reach throughcreateExtension() and raw queries. The AlloyDB dialect has builders for theCloudSQLPostgresDialect extends PostgresDialect (postgres) andObject.getOwnPropertyNames(CloudSQLPostgresDialect.prototype),| Area | Overridden |
|---|---|
| Connection and execution | connect, disconnect, query, resolvePoolConfig |
| Introspection | assertSupported |
| Other | buildSsl |
capabilitiesOf() (src/transaction-support.ts),whereSupportOf() (src/query-builders/expression-render.ts),sqlObjectCapabilitiesOf() (src/sql-objects/capabilities.ts) and the| Capability | cloudsqlpostgres |
|---|---|
| Transactions atomic | yes |
| Savepoints | yes |
| Isolation | up to SERIALIZABLE |
| Transaction modes | read-only yes; deferrable yes; constraint deferral yes |
| RETURNING | RETURNING on INSERT |
| Upsert | INSERT … ON CONFLICT (…) DO UPDATE |
| JSON column | JSON (JSONB → JSONB) |
| Vector column | VECTOR(3) |
| Array column | INTEGER[] |
| WHERE builder | 62 operator keys; $and/$or/$not; $exists: yes; $in subquery: yes; expression operands: yes |
| SQL objects | family postgres: triggers yes; procedures yes; functions multiStatement; materialized views native; sequences yes; enums named; RLS policy |
| Streaming | queryStream() present; LISTEN present; createExtension() present |
tests/dialects/cloudsqlpostgres.test.ts exercises the dialect with a mocked driver, so nothing touches a server.