<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Dinko Osrecki's blog]]></title><description><![CDATA[Dinko Osrecki's blog]]></description><link>https://blog.kodin.hr</link><generator>RSS for Node</generator><lastBuildDate>Tue, 07 Apr 2026 10:02:57 GMT</lastBuildDate><atom:link href="https://blog.kodin.hr/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Deep Dive into Google Cloud SQL Connector for Node.js]]></title><description><![CDATA[Last week Google Cloud announced the general availability of Cloud SQL Node.js connector. This makes it the fourth language-specific connector after Java, Python, and Go. In this article, I will describe the features of the connector, show how it wor...]]></description><link>https://blog.kodin.hr/deep-dive-into-google-cloud-sql-connector-for-nodejs</link><guid isPermaLink="true">https://blog.kodin.hr/deep-dive-into-google-cloud-sql-connector-for-nodejs</guid><category><![CDATA[Google]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[SQL]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Dinko Osrecki]]></dc:creator><pubDate>Mon, 09 Oct 2023 17:18:12 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-sql-nodejs-connector/main/docs/images/cloud-sql-nodejs-connector.png" alt="Source: https://github.com/GoogleCloudPlatform/cloud-sql-nodejs-connector" class="image--center mx-auto" /></p>
<p>Last week <a target="_blank" href="https://cloud.google.com/blog/products/databases/cloud-sql-nodejs-connector-is-ga">Google Cloud announced the general availability</a> of Cloud SQL Node.js connector. This makes it the fourth language-specific connector after Java, Python, and Go. In this article, I will describe the features of the connector, show how it works under the hood, demonstrate how to use it in a Node.js application, and discuss the current support for the connector in the most popular Node.js database libraries.</p>
<h2 id="heading-features">Features</h2>
<p>Cloud SQL Connector is an alternative, and now recommended, way of connecting to Google Cloud SQL instances from Node.js applications, instead of using the Cloud SQL Auth Proxy. It provides the following features, which do not differ from the ones provided by the proxy:</p>
<ol>
<li><p><strong>IAM Authorization</strong>: Use IAM roles to define which user/service accounts can access your instances. Each account must have the <em>Cloud SQL Client</em> role for the instance.</p>
</li>
<li><p><strong>Secure Connection</strong>: The connector establishes a TLS v1.3 connection to your instance, and verifies the identities of both the client and the server. You do not need to manage TLS certificates or firewall rules.</p>
</li>
<li><p><strong>Automatic IAM DB Authentication</strong>: You can use Connector to seamlessly connect to your instances that use IAM database users. Each account must additionally have the <em>Cloud SQL Instance User</em> role for the instance.</p>
</li>
</ol>
<p>It is important to note that Cloud SQL Connector <strong>does not provide a connection path</strong> to your instance. If your instance is on a VPC network behind a private IP, your application workloads must already have access to that VPC network.</p>
<h2 id="heading-how-it-works">How It Works</h2>
<p>To start investigating how the connector works, let’s create a simple Cloud SQL instance with default settings (using public IP connectivity) in Google Cloud Console. It doesn’t matter which database engine we choose, since the connector works with all of them. For this article, I will use PostgreSQL.</p>
<h3 id="heading-port-3307">Port 3307</h3>
<p>Let’s start by scanning the ports of our Cloud SQL instance using <a target="_blank" href="https://nmap.org/"><code>Nmap</code></a>. We will scan standard ports for MySQL, PostgreSQL, and SQL Server.</p>
<pre><code class="lang-bash">&gt; nmap -Pn -p 1433,1434,3306,5432 <span class="hljs-variable">$CLOUD_SQL_INSTANCE_PRIMARY_IP</span>

PORT        STATE       SERVICE
1433/tcp    filtered    ms-sql-s
1434/tcp    filtered    ms-sql-m
3306/tcp    filtered    mysql
5432/tcp    filtered    postgresql
</code></pre>
<p><strong>Standard database server ports are in a <em>filtered</em> state</strong>, which means that a firewall is blocking the access. To connect to the Cloud SQL instance on these ports we need to whitelist our public IP address in the Cloud SQL instance configuration.</p>
<p>Find out your public IP address, and add it to your Cloud SQL instance’s authorized networks in Google Cloud Console. After that, scan the ports again.</p>
<pre><code class="lang-bash">&gt; nmap -Pn -p 1433,1434,3306,5432 <span class="hljs-variable">$CLOUD_SQL_INSTANCE_PRIMARY_IP</span>

PORT        STATE       SERVICE
1433/tcp    filtered    ms-sql-s
1434/tcp    filtered    ms-sql-m
3306/tcp    filtered    mysql
5432/tcp    open        postgresql
</code></pre>
<p>Now, port 5432 is in an <em>open</em> state, which means that we can connect to the Cloud SQL instance using a standard PostgreSQL client.</p>
<p>However, as I already mentioned, one of the benefits of using a Cloud SQL Connector, is that we do not have to manage firewall rules. This is because the <strong>connector uses a special port to connect to the Cloud SQL instance - port 3307</strong>. Let’s scan this port.</p>
<pre><code class="lang-bash">&gt; nmap -Pn -p 3307 <span class="hljs-variable">$CLOUD_SQL_INSTANCE_PRIMARY_IP</span>

PORT        STATE    SERVICE
3307/tcp    open     opsession-prxy
</code></pre>
<p><strong>Port 3307 is always in an <em>open</em> state</strong>, which means that the server is accepting connections on that port without the need to maintain any firewall rules. Try removing your public IP address from the authorized networks, and you will still see port 3307 as open.</p>
<h3 id="heading-ephemeral-certificates">Ephemeral Certificates</h3>
<p>Google Cloud SQL instance runs a proxy service on port 3307. This proxy service is responsible for establishing a secure connection to the database server. Google Cloud SQL Connector communicates with the proxy service using a TLS v1.3 connection. This <strong>connection is secured using an ephemeral X.509 certificate</strong>, which is generated using the Cloud SQL Admin API.</p>
<p>For this to work, you must enable the Cloud SQL Admin API in your Google Cloud project, and the service account that you use to connect to the Cloud SQL instance must have the <em>Cloud SQL Client</em> role.</p>
<p>Internally, the connector first generates an RSA-2048 private/public key pair. It then sends the public key to the Cloud SQL Admin API, which <a target="_blank" href="https://cloud.google.com/sql/docs/mysql/admin-api/rest/v1beta4/connect/generateEphemeralCert"><strong>generates an ephemeral certificate</strong></a> that is signed by the private key that belongs to the Cloud SQL instance. This certificate, together with the generated private key, is then used to create a <a target="_blank" href="https://nodejs.dev/en/api/v18/tls/#tlstlssocket"><code>TLSSocket</code></a> (duplex stream) to the primary IP address of the Cloud SQL instance on port 3307.</p>
<p>Ephemeral certificates are short-lived, and they expire after 1 hour. The connector <strong>automatically renews the certificate</strong> before it expires, as well as when there are connection errors.</p>
<p>If you use IAM database authentication, the connector will additionally obtain the access token, which will be included in the signed ephemeral certificate. For this to work, the service account that you use to connect to the Cloud SQL instance must additionally have the <em>Cloud SQL Instance User</em> role.</p>
<h2 id="heading-usage">Usage</h2>
<p>Since the connector already takes care of establishing a secure connection to the Cloud SQL instance, there must be a way for us to <strong>pass that connection to the database driver</strong>, so that it can use it instead of creating a new one. In fact, we will not be passing an already established connection, but a connection builder which can be used to maintain connection pools by the underlying database drivers.</p>
<p>Cloud SQL Connector class exposes a <code>getOptions</code> method, which returns an object containing the connection builder: <code>{ stream: () ⇒ tls.TLSSocket }</code>. This <a target="_blank" href="https://github.com/brianc/node-postgres/blob/b357e1884ad25b23a4ab034b443ddfc8c8261951/packages/pg/lib/connection.js#L18-L21">connection builder is used by the <em>pg</em> driver</a> to create new connections to the Cloud SQL instance. Similar implementations are available for <a target="_blank" href="https://github.com/sidorares/node-mysql2/blob/ba15fe25703665e516ab0a23af8d828d1473b8c3/lib/connection.js#L63-L65"><em>mysql2</em></a>, and <a target="_blank" href="https://github.com/tediousjs/tedious/blob/443701f35da3bbc4469bdce3168dae28362324a8/src/connection.ts#L2074"><em>tedious</em></a>.</p>
<p>Let’s take a look at a simple example of using the connector with the <em>pg</em> driver to connect to our Cloud SQL instance over public IP, using built-in password authentication.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { AuthTypes, Connector, IpAddressTypes } <span class="hljs-keyword">from</span> <span class="hljs-string">'@google-cloud/cloud-sql-connector'</span>
<span class="hljs-keyword">import</span> pg <span class="hljs-keyword">from</span> <span class="hljs-string">'pg'</span>

<span class="hljs-keyword">const</span> connector = <span class="hljs-keyword">new</span> Connector()
<span class="hljs-keyword">const</span> options = <span class="hljs-keyword">await</span> connector.getOptions({
  instanceConnectionName: <span class="hljs-string">'my-project:region:my-instance'</span>,
  ipType: IpAddressTypes.PUBLIC,
  authType: AuthTypes.PASSWORD
})

<span class="hljs-keyword">const</span> pool = <span class="hljs-keyword">new</span> pg.Pool({
  user: <span class="hljs-string">'my-user'</span>,
  password: <span class="hljs-string">'my-password'</span>,
  database: <span class="hljs-string">'my-database'</span>,
  max: <span class="hljs-number">5</span>,
  ...options
})

<span class="hljs-keyword">const</span> { rows } = <span class="hljs-keyword">await</span> pool.query(<span class="hljs-string">'SELECT true AS is_connected'</span>)
<span class="hljs-built_in">console</span>.table(rows)

<span class="hljs-keyword">await</span> pool.end()
connector.close()
</code></pre>
<p>The code is pretty straightforward. We have called the connector with our SQL instance ID, specifying that we want to use public IP connectivity (instead of private IP) and password authentication (instead of IAM authentication). We then passed the stream builder into the <em>pg</em> Pool constructor so that it can be used whenever a new connection needs to be acquired. If we used IAM authentication we would also omit the <code>password</code> option.</p>
<h3 id="heading-double-encryption"><strong>Double encryption?!</strong></h3>
<p>Depending on the implementation, there is a possibility that the underlying database driver opens <strong>another TLS connection</strong> over the existing TLS connection that was established by the connector, which would have a certain performance impact.</p>
<p>It is therefore beneficial to disable such behavior, if possible. For example, the <em>tedious</em> driver accepts an <code>{ encrypt: boolean = true }</code> option, which should be set to <code>false</code> when using the connector. Cloud SQL Connector class exposes additional <code>getTediousOptions</code> method, which works similarly to the <code>getOptions</code> method, but it also returns the <code>{ encrypt: false }</code> option so that they can both be passed to the <em>tedious</em> driver.</p>
<p>Keep in mind that in this case, you must <strong>uncheck the Allow only SSL connections option</strong> for your Cloud SQL Server instance. Otherwise <em>tedious</em> will find out during the pre-login that the SQL Server requires a TLS connection, but we have set <code>encrypt = false</code>. This will cause an <a target="_blank" href="https://github.com/tediousjs/tedious/blob/443701f35da3bbc4469bdce3168dae28362324a8/src/connection.ts#L3281-L3285">error to be thrown</a>. There is an <a target="_blank" href="https://github.com/GoogleCloudPlatform/cloud-sql-nodejs-connector/issues/230">open issue</a> for this in the Cloud SQL Connector repository. This is not an issue for PostgreSQL and MySQL instances, so you can keep the flag checked for them.</p>
<p>Let's take a look at how these options are passed into the <em>mssql</em> driver in the following code snippet:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Connector, IpAddressTypes } <span class="hljs-keyword">from</span> <span class="hljs-string">'@google-cloud/cloud-sql-connector'</span>
<span class="hljs-keyword">import</span> mssql <span class="hljs-keyword">from</span> <span class="hljs-string">'mssql'</span>

<span class="hljs-keyword">const</span> connector = <span class="hljs-keyword">new</span> Connector()
<span class="hljs-keyword">const</span> options = <span class="hljs-keyword">await</span> connector.getTediousOptions({
  instanceConnectionName: <span class="hljs-string">'my-project:region:my-instance'</span>,
  ipType: IpAddressTypes.PUBLIC
})

<span class="hljs-keyword">const</span> pool = <span class="hljs-keyword">await</span> mssql.connect({
  server: <span class="hljs-string">'REUQIRED-BUT-UNUSED'</span>,
  user: <span class="hljs-string">'my-user'</span>,
  password: <span class="hljs-string">'my-password'</span>,
  database: <span class="hljs-string">'my-database'</span>,
  pool: {
    max: <span class="hljs-number">5</span>,
  },
  options
})

<span class="hljs-keyword">const</span> { recordset } = <span class="hljs-keyword">await</span> mssql.query<span class="hljs-string">`SELECT 'True' AS connected`</span>
<span class="hljs-built_in">console</span>.table(recordset)

<span class="hljs-keyword">await</span> pool.close()
connector.close()
</code></pre>
<p>SQL Server does not support IAM database authentication, so we can only use built-in password authentication. Notice that we also have to pass a dummy <code>server</code> property when creating a connection pool. This is <a target="_blank" href="https://github.com/tediousjs/tedious/issues/1541">due to a bug in the <em>tedious</em> driver</a>, which always requires the <code>server</code> property, even though it is not used when a custom connection builder is provided.</p>
<h2 id="heading-supported-libraries">Supported Libraries</h2>
<p>Cloud SQL Connector is supported in all Node.js database drivers, starting with the versions listed in the table below.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Library</td><td>Version</td></tr>
</thead>
<tbody>
<tr>
<td>pg</td><td>8.9.0</td></tr>
<tr>
<td>mysql2</td><td>0.14.1</td></tr>
<tr>
<td>tedious</td><td>16.1.0</td></tr>
<tr>
<td>mssql</td><td>10.0.0</td></tr>
</tbody>
</table>
</div><p>I also investigated the support for the connector in the most popular high-level database libraries. The connector is mostly supported in the latest versions of these libraries, with some caveats. You can find <a target="_blank" href="https://github.com/edosrecki/google-cloud-sql-nodejs-connector-example">examples of using the connector</a> with all these libraries in my GitHub repository.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Library</td><td>pg</td><td>mysql2</td><td>mssql</td></tr>
</thead>
<tbody>
<tr>
<td>knex</td><td>Yes</td><td>Yes</td><td>Yes</td></tr>
<tr>
<td>sequelize</td><td>Yes</td><td>Yes</td><td>Yes</td></tr>
<tr>
<td>typeorm</td><td>Yes</td><td>Yes</td><td>Yes, butᴬ</td></tr>
<tr>
<td>prisma</td><td>No, butᴮ</td><td>No, butᴮ</td><td>No, butᴮ</td></tr>
</tbody>
</table>
</div><p>ᴬ <em>typeorm</em> officially supports <code>mssql@v9</code>, but the support for the custom stream builder was added in <code>mssql@v10</code>. Since <code>mssql</code> is a peer dependency of <code>typeorm</code>, you can force override it and use the Cloud SQL Connector with <em>typeorm</em>. There is an <a target="_blank" href="https://github.com/typeorm/typeorm/pull/10356">open PR to add support for <code>mssql@v10</code> in <em>typeorm</em></a>.</p>
<p>ᴮ <em>prisma</em> does not support custom connection builders, it only accepts connection strings. Therefore, you need to use a workaround if you wish to use the Cloud SQL Connector with <em>prisma</em>. You do that by <a target="_blank" href="https://github.com/edosrecki/google-cloud-sql-nodejs-connector-example/blob/3b5492637f581f9f8919b78fc83fbdf247684d12/src/prisma/proxy.ts">creating a local TCP proxy that forwards the traffic to the stream</a> created by the Cloud SQL Connector. You then pass a connection string to <em>prisma</em>, which points to the local proxy.</p>
<p>Let's take a look at using the connector with one of the most popular high-level database libraries - <em>knex:</em></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { AuthTypes, Connector, IpAddressTypes } <span class="hljs-keyword">from</span> <span class="hljs-string">'@google-cloud/cloud-sql-connector'</span>
<span class="hljs-keyword">import</span> knex <span class="hljs-keyword">from</span> <span class="hljs-string">'knex'</span>

<span class="hljs-keyword">const</span> connector = <span class="hljs-keyword">new</span> Connector()
<span class="hljs-keyword">const</span> options = <span class="hljs-keyword">await</span> connector.getOptions({
  instanceConnectionName: <span class="hljs-string">'my-project:region:my-instance'</span>,
  ipType: IpAddressTypes.PUBLIC,
  authType: AuthTypes.PASSWORD
})

<span class="hljs-keyword">const</span> database = knex({
  client: <span class="hljs-string">'pg'</span>,
  connection: {
    user: <span class="hljs-string">'my-user'</span>,
    password: <span class="hljs-string">'my-password'</span>,
    database: <span class="hljs-string">'my-database'</span>,
    ...options
  },
})
</code></pre>
<p>There is nothing special going on there - the code snippet is very similar to the one where we used the connector with the <em>pg</em> driver directly.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Cloud SQL Node.js Connector is a great alternative to the Cloud SQL Auth Proxy. It provides similar features but without the need to run a separate Auth Proxy process (VM instance, Kubernetes Deployment, Kubernetes Pod sidecar, …​), which can be <strong>very beneficial if you run your application workloads in a serverless environment</strong> (e.g. Cloud Run).</p>
<p>The connector is supported in all Node.js database drivers and the most popular high-level database libraries. However, if you are using <em>prisma</em>, you most likely want to stay with the Auth Proxy for now, since the workaround to use the connector with <em>prisma</em> is not very elegant.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💻</div>
<div data-node-type="callout-text">For <strong>examples of using the connector</strong> with all the aforementioned database drivers and high-level libraries, <a target="_blank" href="https://github.com/edosrecki/google-cloud-sql-nodejs-connector-example">check out my GitHub repository</a>.</div>
</div>]]></content:encoded></item></channel></rss>