ORMs & Frameworks
Drizzle ORM + QuickBooks Desktop
Use Drizzle ORM for lightweight, type-safe access to QuickBooks Desktop data. Connect via the postgres-js driver with full TypeScript support and schema inference.
New: skip the hand-written schema entirely. Install
See the SDK
@syntraodbc/drizzle-schema for pre-built, typed Drizzle tables for every QuickBooks entity.
Quick Start
- Install Syntra ODBC and ensure the server is running on
localhost:5433. - Install dependencies. Run
npm install drizzle-orm postgresandnpm install -D drizzle-kit. - Create a connection with the postgres-js driver.
- Define or infer your schema and run type-safe queries.
Connection Setup
Create a Drizzle instance using the postgres driver:
// db.ts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
const client = postgres({
host: "localhost",
port: 5433,
database: "quickbooks",
username: "qbconnect",
password: process.env.SYNTRA_PASSWORD, // from config.toml
});
export const db = drizzle(client); Schema Definition
Define your QuickBooks tables with Drizzle's schema builder:
// schema.ts
import { pgTable, text, numeric, boolean, date } from "drizzle-orm/pg-core";
export const customer = pgTable("customers", {
listId: text("list_id").primaryKey(),
fullName: text("full_name").notNull(),
email: text("email"),
phone: text("phone"),
balance: numeric("balance").notNull(),
isActive: boolean("is_active").notNull(),
});
export const invoice = pgTable("invoices", {
txnId: text("txn_id").primaryKey(),
txnDate: date("txn_date").notNull(),
refNumber: text("ref_number"),
customerRefListId: text("customer_ref_list_id").notNull(),
balanceRemaining: numeric("balance_remaining").notNull(),
isPaid: boolean("is_paid").notNull(),
});
export const invoiceLine = pgTable("invoice_lines", {
txnId: text("txn_id").notNull(),
lineId: text("line_id").primaryKey(),
itemRefFullName: text("item_ref_full_name"),
quantity: numeric("quantity"),
rate: numeric("rate"),
amount: numeric("amount"),
}); Schema Introspection with Drizzle Kit
Instead of writing schemas by hand, pull them from Syntra automatically:
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
dbCredentials: {
host: "localhost",
port: 5433,
database: "quickbooks",
user: "qbconnect",
password: process.env.SYNTRA_PASSWORD!,
},
out: "./drizzle",
});
// Then run:
// npx drizzle-kit introspect Type-Safe Queries
Query QuickBooks data with full TypeScript autocompletion:
import { db } from "./db";
import { customer, invoice, invoiceLine } from "./schema";
import { eq, desc, gte, and, sql } from "drizzle-orm";
// Top customers by balance
const topCustomers = await db
.select()
.from(customer)
.where(eq(customer.isActive, true))
.orderBy(desc(customer.balance))
.limit(10);
// Unpaid invoices with customer details
const unpaid = await db
.select({
invoiceNumber: invoice.refNumber,
date: invoice.txnDate,
customerName: customer.fullName,
balance: invoice.balanceRemaining,
})
.from(invoice)
.innerJoin(customer, eq(invoice.customerRefListId, customer.listId))
.where(
and(
eq(invoice.isPaid, false),
gte(invoice.txnDate, "2025-01-01")
)
)
.orderBy(desc(invoice.balanceRemaining));
console.log(`${unpaid.length} unpaid invoices`);
// Revenue by item using raw SQL
const revenueByItem = await db
.select({
item: invoiceLine.itemRefFullName,
totalRevenue: sql`SUM(${invoiceLine.amount})`.as("total"),
lineCount: sql`COUNT(*)`.as("count"),
})
.from(invoiceLine)
.groupBy(invoiceLine.itemRefFullName)
.orderBy(sql`SUM(${invoiceLine.amount}) DESC`)
.limit(20);
console.log("Top items by revenue:", revenueByItem); Tips
- ✓ Use drizzle-kit introspect to auto-generate schemas.
- ✓ Standard plan is read-only. Pro plan enables write operations.
- ✓ The postgres driver supports connection pooling out of the box.
Full reference: Node.js / Drizzle docs →
Type-safe QuickBooks queries with Drizzle
Download Syntra ODBC and start querying with Drizzle ORM in minutes.
Download Free Trial