Connecting to Babelfish from Node.js, Python, and Java
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: falseor equivalent) - Default database:
master(connect without specifying a database, or specifymasterexplicitly)
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
| Feature | mssql (Node.js) | pymssql (Python) | JDBC (Java) |
|---|---|---|---|
| Parameterised queries | Yes | Yes | Yes |
| Batch inserts | Yes | Yes (executemany) | Yes (addBatch) |
| Transaction rollback | Yes | Yes | Yes |
| Stored procedure calls | Yes | Not tested | Not tested |
| Error code propagation | Message text | Error code in tuple | getErrorCode() + getSQLState() |
| TLS on TDS | Not supported | Not supported | Not 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.