22
33![ Typegres playground demo] ( ./assets/demo.gif )
44
5- - ** Methods on Postgres tables = your API.** No routes. No GraphQL. No auto-CRUD.
6- - ** Every Postgres function, fully typed.** All 77 base types, every operator, nullability tracked at the type level.
7- - ** Clients compose typed SQL across the wire.** Server validates the surface area you expose.
5+ - ** Methods on your tables = your API.** No routes. No GraphQL. No auto-CRUD.
6+ - ** Every Postgres/SQLite function, fully typed.** All base types, every operator,
7+ nullability tracked at the type level.
8+ - ** Clients compose typed SQL across the wire.** Server validates the surface
9+ area you expose.
810- ** Live by default.** ` .live() ` re-queries when the underlying data changes — pushed directly to clients.
911
1012> [ typegres.com/play] ( https://typegres.com/play ) · [ demo.mp4] ( ./assets/demo.mp4 ) · [ docs/ARCHITECTURE.md] ( ./docs/ARCHITECTURE.md )
1517> yet recommended for production.
1618
1719``` bash
18- npm install typegres pg
20+ npm install typegres better-sqlite3
1921```
2022
2123``` typescript
22- import { typegres , expose } from " typegres" ;
23- import { Int8 , Text } from " typegres/postgres" ;
24+ import { typegres , expose , sql } from " typegres" ;
25+ import { SqliteDriver } from " typegres/drivers/sqlite" ;
26+ import { Integer , Text } from " typegres/sqlite" ;
2427
25- const { db, conn } = await typegres ({
26- type: " pg" ,
27- connectionString: process .env .DATABASE_URL ! ,
28- });
28+ const db = typegres ();
29+ const conn = db .connect (SqliteDriver .create ());
30+
31+ await conn .execute (sql ` CREATE TABLE users (
32+ id INTEGER PRIMARY KEY,
33+ first_name TEXT NOT NULL,
34+ last_name TEXT NOT NULL
35+ ) ` );
2936
3037class Users extends db .Table (" users" ) {
31- @expose () id = ( Int8 < 1 > ) .column ({ nonNull: true , generated: true });
32- @expose () first_name = ( Text < 1 > ) .column ({ nonNull: true });
33- @expose () last_name = ( Text < 1 > ) .column ({ nonNull: true });
38+ @expose () id = Integer .column ({ nonNull: true , generated: true });
39+ @expose () first_name = Text .column ({ nonNull: true });
40+ @expose () last_name = Text .column ({ nonNull: true });
3441
3542 // Derived column — composes back into your typed query API.
3643 @expose () fullName() {
3744 return this .first_name [" ||" ](" " )[" ||" ](this .last_name );
3845 }
3946}
4047
48+ await Users .insert (
49+ { first_name: " Alice" , last_name: " Smith" },
50+ { first_name: " Bob" , last_name: " Jones" },
51+ ).execute (conn );
52+
4153// `fullName()` works anywhere a column does — select, where, orderBy:
4254const rows = await Users .from ()
4355 .select (({ users }) => ({
@@ -50,43 +62,88 @@ console.log(rows);
5062await conn .close ();
5163```
5264
53- For a complete scaffold with migrations + codegen, see
54- [ ` examples/basic ` ] ( ./ examples/basic ) . Or try it interactively at
65+ For a complete scaffold with migrations + codegen, see the
66+ [ examples] ( # examples) . Or try it interactively at
5567[ typegres.com/play] ( https://typegres.com/play ) .
5668
69+ ## Backends
70+
71+ ` typegres() ` is a synchronous schema handle — no top-level await, so table
72+ classes can be declared at module load. The backend arrives separately via
73+ ` db.connect(driver) ` , and the same schema classes and query builder run
74+ against any of them:
75+
76+ ``` typescript
77+ import { PgDriver } from " typegres/drivers/pg" ; // node-postgres
78+ import { PgliteDriver } from " typegres/drivers/pglite" ; // in-process WASM Postgres
79+ import { SqliteDriver } from " typegres/drivers/sqlite" ; // better-sqlite3
80+ import { DoSqliteDriver } from " typegres/drivers/do" ; // Cloudflare Durable Object
81+
82+ const db = typegres ();
83+
84+ db .connect (PgDriver .create (process .env .DATABASE_URL ! ));
85+ db .connect (SqliteDriver .create (" dev.db" )); // omit the filename for :memory:
86+ db .connect (DoSqliteDriver .create (ctx .storage )); // in the DO constructor — no npm peer needed
87+ db .connect (await PgliteDriver .create ()); // the one async driver: booting WASM is real I/O
88+ ```
89+
90+ Drivers are imported explicitly from ` typegres/drivers/* ` so optional peers
91+ stay out of bundles that never use them — install only the one you need.
92+
93+ With exactly one connection (the Durable Object model), it's also the
94+ default: ` .execute() ` / ` .live() ` take no argument, and you can ignore what
95+ ` connect ` returns. Pass a ` Connection ` explicitly when you have several —
96+ read replicas, database-per-tenant, or a transaction's ` tx ` .
97+
5798## How it works
5899
59- 1 . ** Types codegen'd from the Postgres catalog.** 77 base types, full
100+ 1 . ** Types codegen'd from the Postgres/SQLite catalog/docs .** all base types, full
60101 method/operator coverage, nullability tracked at the type level.
611022 . ** Object-capability queries.** Clients can only reach what you've exposed
62103 as ` @expose ` methods — columns, relations, scoped reads, mutations. The class
63104 surface is the contract; the schema underneath is free to move.
641053 . ** Object-capability RPC.** The query builder ships to a constrained
65106 interpreter on the server; only ` @expose ` -marked methods reach evaluation.
66- 4 . ** Live queries.** ` .live() ` watches the predicates your query depends
67- on and re-yields when committed mutations would change the result.
107+ 4 . ** Live queries.** Tables opt in with ` db.Table("name", { live: true }) ` .
108+ ` .live() ` watches the predicates your query depends on and re-yields when
109+ committed mutations would change the result — via a polling bus on
110+ Postgres, and synchronous mutation capture on SQLite.
68111
69112Deeper dive in [ docs/ARCHITECTURE.md] ( ./docs/ARCHITECTURE.md ) .
70113
114+ ## Examples
115+
116+ - [ ` examples/basic ` ] ( ./examples/basic ) — Postgres/PGLite scaffold:
117+ migrations, ` tg generate ` codegen, relations (` Relation.belongsTo ` / ` .has ` ).
118+ - [ ` examples/sqlite ` ] ( ./examples/sqlite ) — the same scaffold on
119+ better-sqlite3.
120+ - [ ` examples/chat ` ] ( ./examples/chat ) — full-stack chat on a Cloudflare
121+ Durable Object: SQLite storage, Cap'n Web RPC from the browser, live
122+ queries pushed to clients, and facet-based capability security (the whole
123+ server is the schema — there are no routes).
124+
71125## Status
72126
73127- [x] Full pg type system + operator/function codegen
128+ - [x] SQLite dialect — typed function/operator surface from the same
129+ codegen; drivers for better-sqlite3 and Durable Objects
74130- [x] Query builder (` .select ` + ` .join ` + ` .where ` + ` .groupBy ` + ` .having ` + ` .orderBy ` + ` .limit ` )
75131- [x] Mutations (` .insert ` / ` .update ` / ` .delete ` / ` .returning ` )
76132- [x] Subqueries, scalar/array aggregation
77- - [x] Table codegen from live schema
78- - [x] Live queries — ` .live() ` returns an async iterable that
79- re-yields when committed mutations would change the result
133+ - [x] Table codegen from live schema ( ` tg generate ` , both dialects)
134+ - [x] Live queries — ` .live() ` returns a ` LiveQuery ` : an async iterable you
135+ can also ` .observe() ` for push delivery (including over RPC)
80136- [x] Capability-rooted RPC — closures composed against ` @expose ` -marked
81137 classes/methods are serialized, evaluated server-side under a
82- constrained interpreter, and JSON-streamed back
138+ constrained interpreter, and streamed back
139+ - [x] Cap'n Web transport (` typegres/capnweb ` ) — capabilities, promises, and
140+ live subscriptions over a single WebSocket
83141
84142## Planned
85143
86- - [ ] SQLite backend (sql-builder is dialect-aware; adapter is stubbed)
87- - [ ] ` pg_notify ` -driven live updates (currently a single shared polling loop, not per-subscription)
88- - [ ] WAL-mode for live updates (currently uses an auxiliary table)
89- - [ ] Cap'n Web transport (in-flight upstream PR;
144+ - [ ] ` pg_notify ` -driven live updates (Postgres currently uses a single shared polling loop, not per-subscription)
145+ - [ ] WAL-mode live updates for Postgres (currently uses an auxiliary table)
146+ - [ ] Upstream the Cap'n Web integration (in-tree shim today;
90147 [ cloudflare/capnweb #162 ] ( https://github.com/cloudflare/capnweb/pull/162 ) )
91148
92149## Development
0 commit comments