SQL-over-RPC, Safely
A TypeScript API framework that lets clients compose any queries they need within boundaries you control.
1. Decouple Your Interface from Your Schema - With All of Postgres, Fully Typed
Wrap your tables in a stable, public interface. You can refactor your "private" tables and columns without ever breaking clients.
export class User extends Models.User {// Your public interface stays stable as your schema evolvescreatedAt() {return this.metadata['->>']('createdAt').cast(Timestamptz);return this.created_at;}}
// Compiles to the single SQL query you'd write manually.const user = await User.select().orderBy((u) => u.createdAt(), { desc: true }).limit(1).one(tg);
2. Your Interface Defines Your Data Boundaries
Allowed operations are just methods on your interface, including relations and mutations. Everything fully composable and typed.
export class User extends Models.User {todos() {return Todo.select().where((t) => t.user_id.eq(this.id));}}export class Todo extends Models.Todos {update({ completed }: { completed: boolean }) {return update(Todo).set((t) => ({ completed })).where((t) => t.id.eq(this.id));}}
const user = ...// The only way to get a todo is through a user:const todo = await user.todos().where((t) => t.id.eq(todoId)).one(tg);// The only way to update a todo is by getting it from a user:await todo.update({ completed: true }).execute(tg);
3. Expose your API over RPC, SafelyComing Soon
Give clients a composable query builder with your unescapable data boundaries. Compose queries in the client with every Postgres feature (joins, window functions, CTEs, etc.) and function as primitives.
export class User extends Models.User {// ...}export class Todo extends Models.Todos {// ...}export class Api extends RpcTarget {getUserFromToken(token: string) {return User.select((u) => new User(u)).where((u) => u.token.eq(token));}}// Clients receive composable query builders// not flat results
export function TodoList({ searchQuery }: { searchQuery: string }) {const todos = useTypegresQuery((user) => user.todos()// Arbitrarily compose your base query....select((t) => ({ id: t.id, title: t.title }))// ...using any Postgres function such as `ilike`:.where((t) => t.title.ilike(`%${searchQuery}%`)).execute(tg));return (<ul>{todos.map((todo) => (<li key={todo.id}>{todo.title}</li>))}</ul>);}
Ready to build the next generation of database APIs?
Experience the power of composable, capability-first database queries with full type safety and AI-native architecture.
Frequently Asked Questions
API Frameworks: Typegres vs Hasura vs PostgREST
| Typegres | Hasura | PostgREST | |
|---|---|---|---|
| Schema coupling | Decoupled | Tightly coupled | Tightly coupled |
| Client composition | Full composable queries | GraphQL queries | REST endpoints |
| Authorization | Capability-based | RLS + permissions | RLS |
| Refactor safety | Safe schema evolution | Breaking changes | Breaking changes |
| Maturity/Ecosystem | Early/Experimental | Mature | Mature |
Typegres vs ORMs (Prisma, Drizzle, etc.)
Unlike ORMs (which are local dev tools), Typegres is designed for exposing your database over RPC. You can use it alongside your ORM, or as a standalone API layer.
| Typegres | ORMs (Prisma, Drizzle, etc.) | |
|---|---|---|
| Purpose | API framework | Local dev tool |
| Security model | Capability-based | N/A |
| Refactor safety | Safe schema evolution | Breaking changes |
| Maturity/Ecosystem | Early/Experimental | Mature |
Q: What about raw SQL + Row Level Security (RLS)?
tl;dr Raw SQL + RLS has existed for almost a decade. Still, no one lets untrusted SQL run against their database.
A raw SQL approach has many drawbacks, but the most fundamental is applications need application code, not just SQL (e.g., calling external APIs, JSON validation).
Q: What about query performance?
Every query maps directly 1:1 to the single Postgres query you'd expect.
Note that relations are expressed as correlated subqueries, not raw joins, which modern versions of Postgres should optimize. This idea may be revisited later.
Q: How does the RPC layer actually work?
Using the amazing Cap'n Web project. It enables an RPC layer that naturally allows composing over a set of classes/methods safely in a single RPC call.
Q: What's the actual security model under the hood?
The model is capability-based security. Instead of reactive security (a blacklist) the framework explicitly guides you to define your allowed surface area (your classes and methods) and enforces that all queries go through it.
Q: What about DoS?
Currently we recommend query timeouts. (Other options include cost calculations and limiting number of tables allowed per query).
Q: What's the project status?
This is a research preview and not ready for production use. Try the playground to see the latest features and open a discussion or issue on GitHub if you have questions.