Skip to content

Node.js

Node.js connects to Syntra through the bundled Syntra ODBC driver using the odbc package — the standard async ODBC client for Node. No extra configuration beyond installing the package: the Syntra installer already registers the driver and a default DSN system-wide.

  • Syntra ODBC installed and running on localhost:5433 (the default).
  • Node.js 16 or later.
  • A C/C++ toolchain if npm install needs to compile the native binding. On Windows the windows-build-tools package (or Visual Studio Build Tools) is enough.
Terminal window
npm install odbc

The default System DSN the installer creates is named Syntra QuickBooks:

const odbc = require('odbc');
async function main() {
const conn = await odbc.connect('DSN=Syntra QuickBooks');
const rows = await conn.query(`
SELECT full_name, balance, phone, email
FROM customers
WHERE balance > 0
ORDER BY balance DESC
`);
console.table(rows);
await conn.close();
}
main().catch(console.error);

If you prefer driver-direct (no DSN), specify every parameter inline:

const conn = await odbc.connect(
"Driver={Syntra ODBC - QuickBooks ODBC};" +
"Server=127.0.0.1;Port=5433;Database=qbconnect;" +
"Uid=qbconnect;Pwd=changeme;"
);

Uid and Pwd come from the [auth] section of config.toml. Installer defaults are qbconnect / changeme — change them in production.

For any app that serves more than one request at a time, use a pool. The odbc package has a built-in pool:

const odbc = require('odbc');
const pool = await odbc.pool({
connectionString: 'DSN=Syntra QuickBooks',
initialSize: 2,
maxSize: 10,
incrementSize: 1,
});
async function getOpenInvoices() {
const rows = await pool.query(`
SELECT ref_number, customer_ref_full_name, txn_date, balance_remaining
FROM invoices
WHERE is_paid = false
ORDER BY txn_date DESC
LIMIT 100
`);
return rows;
}

Always parameterise. The odbc package uses ? placeholders:

const rows = await pool.query(
'SELECT list_id, full_name FROM customers WHERE balance > ? AND is_active = true',
[1000]
);

The local cache answers most SELECTs. To pull a fresh read straight from QuickBooks, set QB_MAX_STALENESS on the session before running the query:

await conn.query('SET QB_MAX_STALENESS = 0');
const rows = await conn.query('SELECT * FROM customers WHERE list_id = ?', ['80000001-1234567890']);

The setting applies for the lifetime of that connection (or pool member).

Write operations are included on Standard and Pro. INSERT / UPDATE / DELETE go straight to QuickBooks:

await conn.query(
`INSERT INTO customers (name, phone, email) VALUES (?, ?, ?)`,
['Acme Corp', '555-0100', '[email protected]']
);

Multi-line transactions (one invoice with N lines) use the grouped multi-row VALUES pattern. See INSERT / UPDATE / DELETE for full coverage including shared-line parents (bills with item vs expense lines), journal entries, and LinkToTxn for bill-from-sales-order flows.

const express = require('express');
const odbc = require('odbc');
const app = express();
let pool;
(async () => {
pool = await odbc.pool({
connectionString: 'DSN=Syntra QuickBooks',
maxSize: 10,
});
})();
app.get('/api/customers', async (req, res) => {
try {
const rows = await pool.query(
'SELECT full_name, balance, phone, email FROM customers ORDER BY full_name'
);
res.json(rows);
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Database query failed' });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
  • Always pool. Opening a fresh ODBC connection per request adds hundreds of milliseconds.
  • Streaming large results. conn.query buffers the whole result set. For million-row scans use conn.createStatement() + statement.execute and process rows incrementally.
  • TypeScript. The odbc package ships its own types; import odbc from 'odbc' gets you odbc.Pool, odbc.Connection, etc.
  • Timeouts. Append ;Timeout=10 to fail fast on connection. Per-query timeouts aren’t on the connection string — use the statement-level StatementAttributes API.
  • Custom fields surface as custom_* columns. See Custom Fields.