Skip to main content

Connecting to Babelfish from Node.js, Python, and Java

· 7 min read
FoundryDB Team
Engineering @ FoundryDB

Babelfish speaks TDS, the SQL Server wire protocol. Any SQL Server driver connects to it on port 1433 without modification to the driver itself. The only required change from a typical SQL Server connection is disabling TLS on the TDS port: Babelfish 4.8.0 does not support TLS on TDS.

This post shows working connection code and confirms which features work, based on live tests against Babelfish 4.8.0 on PostgreSQL 16.11, running on FoundryDB staging.

For provisioning instructions, see the Babelfish getting-started guide in the documentation.

Connection Basics

Three settings apply to all drivers:

  • Port: 1433
  • TLS: disabled (encrypt: false or equivalent)
  • Default database: master (connect without specifying a database, or specify master explicitly)

The @@VERSION string returned by Babelfish identifies the full environment:

Babelfish for PostgreSQL with SQL Server Compatibility - 12.0.2000.8
Apr 2 2026 15:14:12
Copyright (c) Amazon Web Services
PostgreSQL 16.11 on x86_64-pc-linux-gnu (Babelfish 4.8.0)

SQL Server 2014 wire compatibility (12.0.2000.8) means drivers targeting SQL Server 2014 or later work without any version negotiation issues.


Node.js: mssql

Tested version: mssql@10.0.4

npm install mssql

Connection Config

const sql = require('mssql');

const config = {
server: 'YOUR_HOST',
port: 1433,
user: 'app_user',
password: 'YOUR_PASSWORD',
options: {
encrypt: false,
trustServerCertificate: true,
enableArithAbort: true,
},
connectionTimeout: 15000,
requestTimeout: 15000,
};

enableArithAbort: true avoids deprecation warnings from the driver. encrypt: false and trustServerCertificate: true are both required.

Parameterised Inserts

const pool = await sql.connect(config);

await pool.request()
.input('name', sql.NVarChar(100), 'Alice')
.input('score', sql.Int, 95)
.query('INSERT INTO nodejs_test (name, score) VALUES (@name, @score)');

Parameterised SELECT

const result = await pool.request()
.input('min_score', sql.Int, 90)
.query('SELECT id, name, score FROM nodejs_test WHERE score >= @min_score ORDER BY score DESC');

console.table(result.recordset);

Output from the live test:

┌─────────┬────┬─────────┬───────┐
│ (index) │ id │ name │ score │
├─────────┼────┼─────────┼───────┤
│ 0 │ 1 │ 'Alice' │ 95 │
│ 1 │ 3 │ 'Carol' │ 92 │
└─────────┴─────────┴───────┘

Transactions

const tx = new sql.Transaction(pool);
await tx.begin();
// insert a row ...
await tx.rollback();
// row count unchanged: 3 (confirmed)

Transaction rollback works correctly. Rows inserted within a rolled-back transaction are not persisted.

Stored Procedures

const result = await pool.request()
.input('n', sql.Int, 2)
.execute('usp_top_scorers');
// Returns top 2 rows by score

execute() calls a stored procedure by name with named input parameters. Return sets come back in result.recordsets.

Error Handling

try {
await pool.request().query('SELECT 1/0 AS bad');
} catch (e) {
console.log(e.message); // "division by zero"
}

Babelfish returns PostgreSQL error messages through the TDS layer. The message text is the PostgreSQL error text, not the SQL Server equivalent.

Full Test Run Output

Connecting to bf48-tutorial-4940562e-e3565ebb-db.foundrydb.com:1433...
Connected.

=== @@VERSION ===
Babelfish for PostgreSQL with SQL Server Compatibility - 12.0.2000.8
Apr 2 2026 15:14:12
Copyright (c) Amazon Web Services
PostgreSQL 16.11 on x86_64-pc-linux-gnu (Babelfish 4.8.0)

=== CREATE TABLE ===
Table created.
Parameterised inserts: 3 rows inserted.

=== SELECT (score >= 90) ===
┌─────────┬────┬─────────┬───────┐
│ (index) │ id │ name │ score │
├─────────┼────┼─────────┼───────┤
│ 0 │ 1 │ 'Alice' │ 95 │
│ 1 │ 3 │ 'Carol' │ 92 │
└─────────┴─────────┴───────┘
=== TRANSACTION ===
Rolled back David insert.
Row count after rollback: 3 (expected 3)

=== STORED PROCEDURE (top 2) ===
┌─────────┬─────────┬───────┐
│ (index) │ name │ score │
├─────────┼─────────┼───────┤
│ 0 │ 'Alice' │ 95 │
│ 1 │ 'Carol' │ 92 │
└─────────┴─────────┴───────┘
=== ERROR HANDLING ===
Caught error: division by zero

All tests passed.

Python: pymssql

Tested version: latest from PyPI.

pip install pymssql

Connection

import pymssql

conn = pymssql.connect(
server='YOUR_HOST',
port=1433,
user='app_user',
password='YOUR_PASSWORD',
tds_version='7.3'
)
cursor = conn.cursor()

Specify tds_version='7.3' explicitly. This maps to the SQL Server 2012 wire protocol, which Babelfish handles correctly. Omitting it can cause version negotiation issues depending on your pymssql build.

Parameterised Inserts with executemany

data = [
('Python', 'Django', 78000),
('Python', 'FastAPI', 72000),
('Python', 'Flask', 65000),
]
cursor.executemany(
"INSERT INTO python_test (language, framework, stars) VALUES (%s, %s, %d)",
data
)
conn.commit()

executemany works correctly. All three rows were inserted and confirmed present on a subsequent SELECT.

Parameterised SELECT

cursor.execute("SELECT framework FROM python_test WHERE stars > %d", (70000,))
rows = cursor.fetchall()
# Returns: [('Django',), ('FastAPI',)]

Transactions

conn2 = pymssql.connect(
server='YOUR_HOST',
port=1433,
user='app_user',
password='YOUR_PASSWORD',
tds_version='7.3',
autocommit=False,
)
cur2 = conn2.cursor()
cur2.execute(
"INSERT INTO python_test (language, framework, stars) VALUES ('Ruby', 'Rails', 55000)"
)
conn2.rollback()
# Row count still 3 (confirmed)

Error Handling

try:
cursor.execute("SELECT 1/0")
except pymssql.OperationalError as e:
print(e)
# (8134, b'division by zeroDB-Lib error message 20018, severity 16:\nGeneral SQL Server error: Check messages from the SQL Server\n')

pymssql wraps errors in a DB-Lib message tuple. The SQL Server error code (8134 for division by zero) appears as the first element of the tuple. The second element contains the PostgreSQL error text concatenated with a DB-Lib wrapper message.

Full Test Run Output

Connecting to bf48-tutorial-4940562e-e3565ebb-db.foundrydb.com:1433 ...
Connected.

=== @@VERSION ===
Babelfish for PostgreSQL with SQL Server Compatibility - 12.0.2000.8
...

=== CREATE TABLE ===
Table created.
Parameterised inserts: 3 rows inserted.

=== SELECT (all rows) ===
id language framework stars
------------------------------------
1 Python Django 78000
2 Python FastAPI 72000
3 Python Flask 65000

=== SELECT (stars > 70000) ===
Django
FastAPI

=== TRANSACTION ROLLBACK ===
Row count after rollback: 3 (expected 3)

=== ERROR HANDLING ===
Caught: (8134, b'division by zeroDB-Lib error message 20018...')

All tests passed.

Java: Microsoft JDBC Driver

Tested version: mssql-jdbc 12.6.0.jre11 from Maven Central.

Dependency

Maven:

<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>12.6.0.jre11</version>
</dependency>

Gradle:

implementation 'com.microsoft.sqlserver:mssql-jdbc:12.6.0.jre11'

Connection URL

String url = "jdbc:sqlserver://YOUR_HOST:1433;"
+ "databaseName=master;"
+ "encrypt=false;"
+ "trustServerCertificate=true;";
Connection conn = DriverManager.getConnection(url, "app_user", "YOUR_PASSWORD");

encrypt=false disables TLS on the TDS connection. databaseName=master sets the initial database context explicitly.

PreparedStatement Batch Insert

PreparedStatement ps = conn.prepareStatement(
"INSERT INTO java_test (company, country, revenue) VALUES (?, ?, ?)");

ps.setString(1, "Acme Corp");
ps.setString(2, "USA");
ps.setBigDecimal(3, new BigDecimal("1250000.00"));
ps.addBatch();

ps.setString(1, "Beta GmbH");
ps.setString(2, "Germany");
ps.setBigDecimal(3, new BigDecimal("890000.00"));
ps.addBatch();

ps.setString(1, "Gamma Ltd");
ps.setString(2, "UK");
ps.setBigDecimal(3, new BigDecimal("1100000.00"));
ps.addBatch();

ps.executeBatch();

Batch inserts work correctly. All three rows were confirmed present.

Parameterised SELECT

PreparedStatement ps = conn.prepareStatement(
"SELECT company, revenue FROM java_test WHERE revenue > ?");
ps.setBigDecimal(1, new BigDecimal("1000000"));
ResultSet rs = ps.executeQuery();
while (rs.next()) {
System.out.printf(" %s: %s%n", rs.getString("company"), rs.getBigDecimal("revenue"));
}
// Acme Corp: 1250000.00
// Gamma Ltd: 1100000.00

Transactions

conn.setAutoCommit(false);
// insert a row ...
conn.rollback();
conn.setAutoCommit(true);
// Row count still 3 (confirmed)

Error Handling

try {
st.executeQuery("SELECT 1/0");
} catch (SQLException e) {
System.out.println(e.getSQLState()); // S0001
System.out.println(e.getErrorCode()); // 8134
System.out.println(e.getMessage()); // division by zero
}

The JDBC driver surfaces the SQL Server error code (8134) and SQLState (S0001). This means existing JDBC error handling code that branches on getErrorCode() or getSQLState() works without modification.

Full Test Run Output

Connecting to bf48-tutorial-4940562e-e3565ebb-db.foundrydb.com:1433 ...
Connected.

=== @@VERSION ===
Babelfish for PostgreSQL with SQL Server Compatibility - 12.0.2000.8
...

=== CREATE TABLE ===
Table created.
PreparedStatement batch insert: 3 rows.

=== SELECT (all rows) ===
id company country revenue
---------------------------------------------
1 Acme Corp USA 1250000.00
3 Gamma Ltd UK 1100000.00
2 Beta GmbH Germany 890000.00

=== SELECT (revenue > 1M) ===
Acme Corp: 1250000.00
Gamma Ltd: 1100000.00

=== TRANSACTION ROLLBACK ===
Row count after rollback: 3 (expected 3)

=== ERROR HANDLING ===
Caught SQLState=S0001 code=8134 msg=division by zero

All tests passed.

Summary

Featuremssql (Node.js)pymssql (Python)JDBC (Java)
Parameterised queriesYesYesYes
Batch insertsYesYes (executemany)Yes (addBatch)
Transaction rollbackYesYesYes
Stored procedure callsYesNot testedNot tested
Error code propagationMessage textError code in tuplegetErrorCode() + getSQLState()
TLS on TDSNot supportedNot supportedNot supported

All three drivers connect to the same TDS endpoint on port 1433. The only connection change from a standard SQL Server setup is encrypt=false.

Get Started

Provision a Babelfish instance on FoundryDB and connect using any standard SQL Server driver. No driver modifications required.