Skip to content

effect-orm

Type-safe ClickHouse, Postgres, SQLite and MySQL queries, result decoding, and reproducible benchmarks for Effect and TypeScript.

Updated

@maple-dev/effect-orm builds SQL from typed TypeScript. Describe a table once, and the query builder infers column types, output row shapes and join accessors from it. A query compiles to a SQL string plus a decoder for its own rows. You bring the database client.

Install

npm i @maple-dev/effect-orm effect@^4.0.6

effect is a peer dependency. Effect 3 and the Effect 4 prereleases are not supported.

A first query

Each database has one import that holds the whole query builder plus that database’s column types, functions, table definitions and compile:

import * as CH from "@maple-dev/effect-orm/clickhouse"

const Events = CH.table("events", {
	columns: {
		OrgId: CH.string,
		Name: CH.string,
		Timestamp: CH.dateTime,
		DurationMs: CH.uint64,
	},
	engine: CH.engine.mergeTree(),
	orderBy: ["OrgId", "Timestamp"],
})

const query = CH.from(Events)
	.select(($) => ({
		name: $.Name,
		p95: CH.quantile(0.95)($.DurationMs),
		count: CH.count(),
	}))
	.where(($) => [$.OrgId.eq(CH.param.string("orgId"))])
	.groupBy("name")
	.orderBy(["count", "desc"])
	.limit(50)

const compiled = CH.compileUnsafe(query, { orgId: "org_123" })
compiled.sql // SELECT events.Name AS name, quantile(0.95)(events.DurationMs) AS p95, ...

Why effect-orm

Schema-first

A column type is an Effect Schema, so a query compiles to its own row schema. Wire quirks, such as 64-bit integers arriving quoted, are modelled once in the types.

Type-safe

Column types, output row shapes and join accessors are inferred from the table. No stringly-typed columns.

Immutable and composable

Every builder method returns a new query, so you can share and extend base queries.

Four databases

ClickHouse, Postgres, SQLite and Cloudflare D1, MySQL and MariaDB. Each has its own column types, functions and DDL.

Pick your database

Every database has its own section with the same guides, written with its own examples. The tabs at the top switch between them and keep you on the page you are reading.

Benchmarks and the export reference are shared by every database.

Type to search…

↑↓ navigate↵ selectEsc close