PostgreSQL-only stack
PostgREST is purpose-built for PostgreSQL. If you are not using other databases and do not need GraphQL or gRPC, it has less overhead.
Both FraiseQL and PostgREST turn a PostgreSQL database into an HTTP API. They reflect fundamentally different philosophies about how that API should be defined.
| Feature | FraiseQL | PostgREST |
|---|---|---|
| REST | CQRS-derived + annotation override | Auto-wired from schema |
| Response format | JSON envelope (data/meta/links) | Unwrapped JSON arrays |
| Filtering | Bracket operators, JSON DSL, simple equality | Operator syntax (eq., gt., etc.) |
| Pagination | Offset + relay/cursor with meta/links | Range header + limit/offset |
| Field selection | ?select=id,name | ?select=id,name |
| ETags | Yes (xxHash64, If-None-Match → 304) | Yes |
Prefer header | return, count=exact | return, count, handling, resolution |
| GraphQL | Yes | No |
| gRPC | Yes (requires grpc-transport feature flag) | No |
| WebSocket subscriptions | Yes | No |
| Schema authoring | 11 SDK languages | PostgreSQL schema |
| Database support | PostgreSQL, MySQL, SQLite, SQL Server | PostgreSQL only |
| Runtime | Compiled Rust binary | Haskell runtime |
| REST endpoint control | CQRS-derived + explicit annotations | Auto-expose all tables/views |
| OpenAPI generation | Yes (3.0.3) | Yes |
| Authentication | JWT/OIDC/API keys | JWT via PostgreSQL RLS |
| Embedding | ?select=id,orders(id,total) with nesting + rename + count | ?select=*,orders(*) |
| Bulk operations | POST /bulk, filter-based PATCH/DELETE, upsert | Yes |
| Full-text search | ✓ (?search=term) | ?fts=term |
| License | Apache 2.0 | MIT |
PostgREST automatically exposes PostgreSQL tables and views as REST endpoints. Every table, every view is immediately accessible — zero annotation work required.
FraiseQL requires explicit rest_path and rest_method annotations on each operation. You decide what is exposed, what parameters it accepts, and what it returns. Nothing is exposed implicitly.
Neither approach is better universally. They reflect different philosophies about the contract between your database schema and your public API surface.
PostgreSQL-only stack
PostgREST is purpose-built for PostgreSQL. If you are not using other databases and do not need GraphQL or gRPC, it has less overhead.
Instant REST from existing tables
Point PostgREST at an existing database and every table becomes a REST endpoint immediately. No annotation work, no schema authoring.
REST-only requirements
If your API consumers only need REST and you have no plans to add GraphQL or gRPC, PostgREST’s focus is an asset rather than a limitation.
Database schema as source of truth
If your team prefers defining everything in PostgreSQL — types, constraints, RLS policies — and treating the database schema as the single source of truth, PostgREST aligns with that workflow.
REST + GraphQL + gRPC from one binary
All three transports from a single compiled binary. No separate services to deploy, no duplicate auth configuration.
Explicit API surface control
Only what you annotate is exposed. Adding a table to the database does not automatically create a public endpoint. The REST surface is visible in code review.
Multi-database support
MySQL, SQL Server, and SQLite alongside PostgreSQL. PostgREST is PostgreSQL-only.
SDK-driven schema authoring
Define your schema in Python, TypeScript, Go, or any of the other supported SDKs. Native type safety in the language your team already uses.
Auto-generated OpenAPI + introspection + .proto
All three API contract formats generated from the same annotated schema. No manual YAML or .proto files.
PostgREST’s auto-exposure model means any table or view added to the database immediately has a REST endpoint (subject to PostgreSQL RLS). The API surface is defined in the database.
FraiseQL’s annotation model means the REST surface is defined in your schema code — versioned, visible in pull requests, auditable. Adding a table to the database does not change the API unless you also add an annotated query.
Both approaches are valid. The question is where your team wants API surface decisions to live: in the database schema (PostgREST) or in application code (FraiseQL).
In PostgREST, creating a view exposes it immediately:
-- PostgREST: this view is now accessible at GET /usersCREATE VIEW users AS SELECT id, email FROM accounts;In FraiseQL, the view exists but is not exposed until annotated:
# FraiseQL: view exists in the database, but nothing is exposed until this annotation@fraiseql.query( sql_source="v_user", rest_path="/users", rest_method="GET",)def users(limit: int = 100) -> list[User]: ...The extra step is intentional. The exposed API surface is auditable in version control.
Migrate from PostgREST
Step-by-step guide to adopting FraiseQL annotation-driven REST alongside GraphQL.