🧪 Experimental | The RPC layer is under active development. Not ready for production use. Star on GitHub for updates →

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.

api.ts
export class User extends Models.User {
// Your public interface stays stable as your schema evolves
createdAt() {
return this.metadata['->>']('createdAt').cast(Timestamptz);
return this.created_at;
}
}
route.ts
// 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.

api.ts
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));
}
}
route.ts
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.

api.ts
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
frontend.tsx
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

TypegresHasuraPostgREST
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.

TypegresORMs (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.