Skip to content

FraiseQL vs PostgREST

Both FraiseQL and PostgREST turn a PostgreSQL database into an HTTP API. They reflect fundamentally different philosophies about how that API should be defined.

FeatureFraiseQLPostgREST
RESTCQRS-derived + annotation overrideAuto-wired from schema
Response formatJSON envelope (data/meta/links)Unwrapped JSON arrays
FilteringBracket operators, JSON DSL, simple equalityOperator syntax (eq., gt., etc.)
PaginationOffset + relay/cursor with meta/linksRange header + limit/offset
Field selection?select=id,name?select=id,name
ETagsYes (xxHash64, If-None-Match → 304)Yes
Prefer headerreturn, count=exactreturn, count, handling, resolution
GraphQLYesNo
gRPCYes (requires grpc-transport feature flag)No
WebSocket subscriptionsYesNo
Schema authoring11 SDK languagesPostgreSQL schema
Database supportPostgreSQL, MySQL, SQLite, SQL ServerPostgreSQL only
RuntimeCompiled Rust binaryHaskell runtime
REST endpoint controlCQRS-derived + explicit annotationsAuto-expose all tables/views
OpenAPI generationYes (3.0.3)Yes
AuthenticationJWT/OIDC/API keysJWT via PostgreSQL RLS
Embedding?select=id,orders(id,total) with nesting + rename + count?select=*,orders(*)
Bulk operationsPOST /bulk, filter-based PATCH/DELETE, upsertYes
Full-text search✓ (?search=term)?fts=term
LicenseApache 2.0MIT

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

Auto-exposure vs annotation: a concrete example

Section titled “Auto-exposure vs annotation: a concrete example”

In PostgREST, creating a view exposes it immediately:

-- PostgREST: this view is now accessible at GET /users
CREATE 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.

Read guide