From dd64d0007f3d9a039ebb15303e09290ef66f3a10 Mon Sep 17 00:00:00 2001 From: PedroHenrique0713 Date: Tue, 1 Sep 2026 17:08:17 -0300 Subject: [PATCH] docs: add Drizzle ORM to third-party tools Closes #70. Picks up where #71 stopped: that PR had the maintainer's go-ahead and only needed the example aligned to the trades table, but it went stale and was closed six months later without landing. Everything documented here was run against QuestDB 10.0.1 with drizzle-orm 0.45.2 and postgres 3.4.9, so the page states what the combination actually does rather than what it should do: - insert, select with where/orderBy/limit, and groupBy aggregation all work through the query builder - SAMPLE BY has no builder equivalent and goes through db.execute(sql``) - DELETE fails, since QuestDB has no row-level delete; the page points at dropping a partition instead - migrations are out, because the table needs a designated timestamp and partitioning that drizzle-kit does not express Uses the trades table throughout, matching the other client and integration pages. --- documentation/integrations/other/drizzle.md | 151 ++++++++++++++++++++ documentation/integrations/overview.md | 2 + documentation/sidebars.js | 1 + 3 files changed, 154 insertions(+) create mode 100644 documentation/integrations/other/drizzle.md diff --git a/documentation/integrations/other/drizzle.md b/documentation/integrations/other/drizzle.md new file mode 100644 index 0000000000..c38d22f688 --- /dev/null +++ b/documentation/integrations/other/drizzle.md @@ -0,0 +1,151 @@ +--- +title: Drizzle ORM +description: Guide for using Drizzle ORM with QuestDB +--- + +[Drizzle ORM](https://orm.drizzle.team/) is a lightweight, type-safe SQL ORM for +TypeScript and JavaScript. It connects to QuestDB over the +[PostgreSQL wire protocol](/docs/configuration/postgres-wire-protocol/), so the standard +`postgres-js` driver works without a QuestDB-specific dialect. + +Drizzle is a good fit for reading from QuestDB in a TypeScript application. For +high-throughput ingestion, prefer the +[Node.js client](/docs/connect/clients/nodejs/), which uses InfluxDB Line Protocol +and is built for that purpose. + +Note that QuestDB is a time-series database, not a general-purpose relational +one. Some Drizzle operations are unsupported as a result, listed under +[Limitations](#limitations). + +## Prerequisites + +- Node.js 18 or newer +- A running QuestDB instance +- `drizzle-orm` and `postgres` + +## Installation + +```shell +npm install drizzle-orm postgres +``` + +## Connecting + +QuestDB serves the PostgreSQL wire protocol on port `8812`, with `admin`/`quest` +as the default credentials and `qdb` as the database name. + +```typescript +import { drizzle } from "drizzle-orm/postgres-js" +import postgres from "postgres" + +const client = postgres({ + host: "localhost", + port: 8812, + database: "qdb", + username: "admin", + password: "quest", +}) + +const db = drizzle(client) +``` + +## Defining a table + +QuestDB tables are created with SQL rather than through Drizzle migrations, +since the table needs a designated timestamp and a partitioning strategy that +Drizzle's schema builder does not express: + +```questdb-sql +CREATE TABLE trades ( + symbol SYMBOL, + side SYMBOL, + price DOUBLE, + amount DOUBLE, + timestamp TIMESTAMP +) TIMESTAMP(timestamp) PARTITION BY DAY WAL; +``` + +Declare the matching Drizzle schema to query it. `SYMBOL` columns are read as +text: + +```typescript +import { pgTable, doublePrecision, text, timestamp } from "drizzle-orm/pg-core" + +export const trades = pgTable("trades", { + symbol: text("symbol"), + side: text("side"), + price: doublePrecision("price"), + amount: doublePrecision("amount"), + timestamp: timestamp("timestamp"), +}) +``` + +## Inserting rows + +```typescript +await db.insert(trades).values([ + { + symbol: "BTC-USD", + side: "buy", + price: 39269.98, + amount: 0.001, + timestamp: new Date(), + }, +]) +``` + +Writes go through the [WAL](/docs/concepts/write-ahead-log/), so a row may take +a moment to become visible to readers. + +## Querying + +```typescript +import { desc, eq, sql } from "drizzle-orm" + +// Latest trade for a symbol +const latest = await db + .select() + .from(trades) + .where(eq(trades.symbol, "BTC-USD")) + .orderBy(desc(trades.timestamp)) + .limit(1) + +// Average price per symbol +const averages = await db + .select({ symbol: trades.symbol, avg: sql`avg(${trades.price})` }) + .from(trades) + .groupBy(trades.symbol) +``` + +## QuestDB SQL extensions + +Time-series extensions such as +[`SAMPLE BY`](/docs/query/sql/sample-by/) have no Drizzle query-builder +equivalent. Use `db.execute()` with a raw statement, which keeps the same +connection and pooling: + +```typescript +import { sql } from "drizzle-orm" + +const perMinute = await db.execute( + sql`SELECT timestamp, avg(price) FROM trades SAMPLE BY 1m`, +) +``` + +The same applies to [`LATEST ON`](/docs/query/sql/latest-on/) and +[`ASOF JOIN`](/docs/query/sql/join/#asof-join). + +## Limitations + +- **`DELETE` is not supported.** QuestDB has no row-level delete, so + `db.delete(...)` fails. Remove data by + [dropping a partition](/docs/query/sql/alter-table-drop-partition/) instead. +- **Migrations are not supported.** Use SQL DDL for schema changes, as shown + above, rather than `drizzle-kit`. +- **Relations and joins** are limited to what QuestDB's SQL supports; there are + no foreign keys. + +## Version note + +The examples above were verified against QuestDB 10.0.1 with `drizzle-orm` +0.45.2 and `postgres` 3.4.9. diff --git a/documentation/integrations/overview.md b/documentation/integrations/overview.md index 50f8ed528c..d956e5ec69 100644 --- a/documentation/integrations/overview.md +++ b/documentation/integrations/overview.md @@ -69,6 +69,8 @@ Improve your interactions with QuestDB using these tools and interfaces: analyze monitoring metrics. - [SQLAlchemy](/docs/integrations/other/sqlalchemy/): Utilize Python's ORM capabilities for database interactions. +- [Drizzle ORM](/docs/integrations/other/drizzle/): Query QuestDB from + TypeScript with a type-safe ORM over the PostgreSQL wire protocol. - [MindsDB](/docs/integrations/other/mindsdb/): Build machine learning models for predictive analytics on [time-series data](/blog/what-is-time-series-data/). - [Databento](/docs/integrations/other/databento/): Ingest a normalized live diff --git a/documentation/sidebars.js b/documentation/sidebars.js index d518a599c7..30981e8280 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -927,6 +927,7 @@ module.exports = { collapsed: true, items: [ "integrations/other/prometheus", + "integrations/other/drizzle", "integrations/other/sqlalchemy", "integrations/other/mindsdb", "integrations/other/databento",