NpgsqlRest vs PostgREST vs Supabase: Complete Feature Comparison
Choosing the right tool to expose your PostgreSQL database as a REST API can significantly impact your project's performance, maintainability, and development velocity. This comprehensive comparison examines three popular solutions: NpgsqlRest, PostgREST, and Supabase.
Executive Summary
| Aspect | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| What It Is | Standalone executable/Docker | Standalone executable/Docker | Backend-as-a-Service platform |
| Core Focus | Function-first REST API | Table/View-centric REST API | Complete backend platform |
| Best For | Enterprise features, custom paths | Flexible client-side queries | Full-stack with managed hosting |
| Performance | 5,177 req/s (100 VU) | 843 req/s (100 VU) | Uses PostgREST internally |
| Deployment | Single binary (~30MB) | Single binary (~20MB) | Multiple services required |
| Self-Hosting | Simple | Simple | Complex (7+ services) |
| Learning Curve | Low (SQL comments) | Medium (RLS policies) | Medium (platform concepts) |
Architecture Comparison
NpgsqlRest
┌─────────────┐ ┌─────────────────┐ ┌────────────┐
│ Client │────▶│ NpgsqlRest │────▶│ PostgreSQL │
│ │◀────│ (30MB AOT) │◀────│ │
└─────────────┘ └─────────────────┘ └────────────┘
Single executableNpgsqlRest is a single, self-contained executable that connects directly to PostgreSQL and serves REST APIs. No additional infrastructure required. Download, configure connection string, run.
PostgREST
┌─────────────┐ ┌─────────────────┐ ┌────────────┐
│ Client │────▶│ PostgREST │────▶│ PostgreSQL │
│ │◀────│ (Haskell) │◀────│ │
└─────────────┘ └─────────────────┘ └────────────┘
Single executablePostgREST follows a similar single-binary model. Both are lightweight and straightforward to deploy.
Supabase
┌─────────────┐ ┌─────────────────────────────────────────────┐
│ Client │────▶│ Supabase │
│ │◀────│ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │
└─────────────┘ │ │PostgREST│ │ GoTrue │ │ Realtime │ │
│ └─────────┘ └─────────┘ └──────────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │
│ │ Storage │ │ Kong │ │ Studio │ │
│ └─────────┘ └─────────┘ └──────────────┘ │
│ ┌────────────┐ │
│ │ PostgreSQL │ │
│ └────────────┘ │
└─────────────────────────────────────────────┘Supabase is a platform composed of multiple services. PostgREST handles REST API generation, GoTrue manages authentication, Kong provides API gateway functionality, and additional services handle storage, realtime, and the admin dashboard. Self-hosting requires orchestrating all these components.
Performance Benchmarks
Benchmark results from PostgreSQL REST API Benchmark 2025, testing 15 frameworks under identical conditions:
Requests Per Second (100 Concurrent Users, 1 Record)
| Framework | Requests/sec | Latency | Scaling Factor |
|---|---|---|---|
| NpgsqlRest JIT | 5,177 | 9.68ms | 8.6x |
| NpgsqlRest AOT | 2,622 | 19.08ms | 4.2x |
| Rust (Actix) | 3,452 | 14.47ms | 6.1x |
| PostgREST | 843 | 59.34ms | 2.2x |
Key findings:
- NpgsqlRest JIT is 6.1x faster than PostgREST at 100 concurrent users
- PostgREST scales poorly: from 385 req/s (1 user) to only 843 req/s (100 users) - a 2.2x improvement
- NpgsqlRest JIT scales excellently: from 601 req/s (1 user) to 5,177 req/s (100 users) - an 8.6x improvement
Larger Payloads (500 Records, 100 VU)
| Framework | Requests/sec | Latency |
|---|---|---|
| NpgsqlRest JIT | 100.90 | 495ms |
| PostgREST | 45.57 | 1,098ms |
Even with larger payloads where database I/O dominates, NpgsqlRest processes 2.2x more requests with less than half the latency.
PostgreSQL Type Handling
| Type | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| JSON/JSONB | ✅ | ✅ | ✅ |
| Arrays (int[], text[]) | ✅ | ✅ | ✅ |
| Composite types | ✅ | ✅ | ✅ |
| Date/Time types | ✅ | ✅ | ✅ |
| Boolean | ✅ | ✅ | ✅ |
| Variadic parameters | ✅ | ✅ | ✅ |
| OUT parameters | ✅ | ✅ | ✅ |
| Default parameters | ✅ | ✅ | ✅ |
All three frameworks handle PostgreSQL types correctly.
Feature Comparison Matrix
Core API Generation
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| Functions as endpoints | ✅ | ✅ | ✅ |
| Tables as endpoints | ✅ | ✅ | ✅ |
| Views as endpoints | ✅ | ✅ | ✅ |
| Function overloading | ✅ | ✅ | ✅ |
| Custom URL paths | ✅ | ❌ | ❌ |
Path parameters (/users/{id}) | ✅ | ❌ | ❌ |
| OpenAPI/Swagger generation | ✅ | ✅ | ✅ |
| TypeScript client generation | ✅ | ❌ | ✅ |
| HTTP test file generation | ✅ | ❌ | ❌ |
Table and View Query Features
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| Basic CRUD (SELECT/INSERT/UPDATE/DELETE) | ✅ | ✅ | ✅ |
| INSERT RETURNING | ✅ | ✅ | ✅ |
| ON CONFLICT (upsert) | ✅ | ✅ | ✅ |
| Client-side filtering operators | ❌ | ✅ (28+ operators) | ✅ |
| Client-side resource embedding | ❌ | ✅ | ✅ |
| Client-side aggregates | ❌ | ✅ | ✅ |
| Client-side column selection | ❌ | ✅ | ✅ |
| Client-side ordering/pagination | ❌ | ✅ | ✅ |
PostgREST and Supabase allow clients to compose queries against tables:
- Resource embedding automatically joins related tables based on foreign keys
- 28+ filtering operators including
like,ilike,in,fts(full-text search), range operators - Aggregate functions with automatic GROUP BY
NpgsqlRest takes a function-first approach: these same capabilities (joins, full-text search, aggregates, filtering, pagination) are achieved through PostgreSQL functions. This is a deliberate design choice, not a limitation:
- Full PostgreSQL power - Every query feature PostgreSQL offers is available: CTEs, window functions, recursive queries, full-text search, JSON operators, lateral joins—anything you can write in SQL
- Optimized query plans - PostgreSQL optimizes your functions; client-composed queries may produce suboptimal plans
- Security by design - You expose exactly what you intend to expose, not an entire table with filters
- Predictable performance - No surprise queries that scan entire tables or create N+1 problems
- Easier with modern AI tools - Writing PostgreSQL functions is straightforward, especially with AI assistance
Author's note: NpgsqlRest was designed with functions and stored procedures as the primary abstraction layer. Table/view endpoints exist for convenience, but I personally consider exposing raw tables directly to be a questionable engineering practice. Functions provide a proper API contract, encapsulate business logic, and give you full control over what data is exposed and how. — Vedran Bilopavlović, NpgsqlRest author
Choose PostgREST if you want clients to compose their own queries against tables. Choose NpgsqlRest if you prefer well-defined server-side endpoints with explicit query logic.
Custom Types and Nested JSON
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| Return composite types as JSON | ✅ | ✅ | ✅ |
| Return SETOF composite types | ✅ | ✅ | ✅ |
| Nested composite types in response | ✅ | ✅ | ✅ |
| Arrays of composite types (multiset) | ✅ | ✅ | ✅ |
| Deep nesting (3+ levels) | ✅ | ✅ | ✅ |
| Flat/merged composite mode | ✅ | ❌ | ❌ |
| Composite type as parameter | ✅ | ✅ | ✅ |
| Parameter field unnesting | ✅ | ❌ | ❌ |
| TypeScript types for nested structures | ✅ | ❌ | ⚠️ |
✅ = Full support ⚠️ = Partial support or workarounds needed ❌ = Not supported
All three frameworks support PostgreSQL composite types and can return nested JSON structures, including arrays of composite types (multiset pattern). Key differences:
NpgsqlRest offers two modes: By default, composite type fields are merged/flattened into the parent object for simpler responses. Use the
@nestedannotation to preserve the hierarchical structure. PostgREST/Supabase always nest composite types.Parameter unnesting: NpgsqlRest automatically expands composite type parameters into individual named fields (e.g.,
authorFirstName,authorLastName), making API consumption more intuitive. PostgREST/Supabase require passing composite parameters as nested JSON objects.TypeScript generation: NpgsqlRest generates proper TypeScript interfaces for all nested structures automatically. Supabase generates types but may require manual casting for complex RPC return types. PostgREST has no built-in TypeScript generation (third-party tools like kanel can help).
Deep nesting: All three frameworks now handle arbitrary nesting depths. NpgsqlRest 3.4.4+ resolves nested composite types to any depth by default (configurable via
ResolveNestedCompositeTypes). The only remaining limitation is 2D arrays of composite types at the return type level, which remain as tuple strings.
See Custom Types and Multiset for Nested JSON for detailed examples.
Authentication
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| JWT authentication | ✅ | ✅ | ✅ |
| Cookie authentication | ✅ | ❌ | ✅ |
| Bearer token authentication | ✅ | ✅ | ✅ |
| HTTP Basic authentication | ✅ | ❌ | ❌ |
| OAuth providers (Google, GitHub, etc.) | ✅ | ❌ | ✅ |
| Custom login/logout functions | ✅ | ❌ | ✅ |
| Password hashing (PBKDF2) | ✅ | ❌ | ✅ |
| Multiple auth schemes simultaneously | ✅ | ❌ | ✅ |
| Role-based access control | ✅ | ✅ | ✅ |
| Claims to PostgreSQL context | ✅ | ✅ | ✅ |
| Claims to PostgreSQL parameters | ✅ | ❌ | ❌ |
NpgsqlRest supports 4 authentication schemes (Cookie, Bearer, JWT, Basic Auth) plus external OAuth providers, all configurable via JSON. Authentication logic lives in your PostgreSQL functions - you control everything.
NpgsqlRest also offers claims-to-parameters mapping - user claims (user ID, roles, custom claims) are automatically injected as function parameters by configurable name matching, with type safety enforced by PostgreSQL. PostgREST and Supabase only support accessing claims via current_setting() or auth.jwt() context functions, requiring manual casting within your SQL code.
PostgREST only supports JWT - you need an external auth server like GoTrue (which is what Supabase uses).
File Handling
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| File uploads | ✅ | ❌ | ✅ |
| Image uploads with validation | ✅ | ❌ | ✅ |
| PostgreSQL Large Objects | ✅ | ❌ | ❌ |
| File system storage | ✅ | ❌ | ✅ |
| CSV file ingestion | ✅ | ❌ | ❌ |
| Excel file ingestion | ✅ | ❌ | ❌ |
| CSV export | ✅ | ✅ | ✅ |
| Row-by-row processing | ✅ | ❌ | ❌ |
| Upload metadata to functions | ✅ | ❌ | ❌ |
NpgsqlRest provides comprehensive file handling including:
- Image uploads with format validation (JPEG, PNG, GIF, WebP, etc.)
- CSV/Excel ingestion with row-by-row processing through PostgreSQL functions
- PostgreSQL Large Objects for storing files directly in the database
- Combined handlers (multiple storage backends in a single transaction)
PostgREST has no file upload support. Supabase requires a separate Storage service.
Performance Features
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| Response caching (memory) | ✅ | ❌ | ❌ |
| Response caching (Redis) | ✅ | ❌ | ❌ |
| Hybrid cache with stampede protection | ✅ | ❌ | ❌ |
| Cache invalidation endpoints | ✅ | ❌ | ❌ |
| Rate limiting | ✅ | ❌ | ✅ |
| Multiple rate limit algorithms | ✅ | ❌ | ❌ |
| Connection pooling | ✅ | ✅ | ✅ |
| Command retry with backoff | ✅ | ❌ | ❌ |
| Multi-host failover | ✅ | ❌ | ✅ |
| Request compression | ✅ | ✅ | ✅ |
| Response compression (Gzip/Brotli) | ✅ | ✅ | ✅ |
NpgsqlRest includes enterprise-grade performance features:
- Three caching modes: Memory, Redis, and Hybrid with stampede protection
- Four rate limiting algorithms: Fixed window, sliding window, token bucket, concurrency
- Automatic retry with exponential backoff for transient failures
- Multi-host failover with read replica support
Real-Time Capabilities
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| Server-Sent Events (SSE) | ✅ | ❌ | ❌ |
| WebSockets | ❌ | ❌ | ✅ |
| PostgreSQL RAISE streaming | ✅ | ❌ | ❌ |
| PostgreSQL LISTEN/NOTIFY | ❌ | ❌ | ✅ |
| Broadcast to subscribed clients | ✅ | ❌ | ✅ |
| Event scoping (authorize/matching/all) | ✅ | ❌ | ❌ |
NpgsqlRest uses Server-Sent Events for real-time streaming, which is simpler than WebSockets and works through standard HTTP. PostgreSQL's RAISE INFO/NOTICE/WARNING statements stream directly to connected clients - no message brokers or LISTEN/NOTIFY infrastructure needed. See the Real-Time Chat example.
Advanced Features
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| Per-endpoint configuration in DB | ✅ | ❌ | ❌ |
| Reverse proxy mode | ✅ | ❌ | ❌ |
| Proxy to external services | ✅ | ❌ | ✅ |
| HTTP calls from PostgreSQL | ✅ | ❌ | ✅ |
| Custom response headers | ✅ | ✅ | ✅ |
| Request header forwarding | ✅ | ✅ | ✅ |
| Per-endpoint timeouts | ✅ | ❌ | ❌ |
| Per-endpoint caching policies | ✅ | ❌ | ❌ |
| Per-endpoint rate limiting | ✅ | ❌ | ❌ |
| Per-endpoint retry strategies | ✅ | ❌ | ❌ |
| Parameter validation | ✅ | ❌ | ❌ |
| Raw (non-JSON) responses | ✅ | ✅ | ✅ |
| Error code mapping | ✅ | ✅ | ✅ |
| CORS configuration | ✅ | ✅ | ✅ |
| HTTPS/TLS | ✅ | ✅ | ✅ |
NpgsqlRest's reverse proxy feature allows PostgreSQL functions to define upstream services, enabling patterns like:
- Proxying to AI services (OpenAI, Anthropic) with response caching
- API aggregation from multiple external sources
- Response transformation and enrichment
Observability
| Feature | NpgsqlRest | PostgREST | Supabase |
|---|---|---|---|
| Structured logging | ✅ | ✅ | ✅ |
| Log to file | ✅ | ❌ | ✅ |
| Log to PostgreSQL | ✅ | ❌ | ✅ |
| OpenTelemetry | ✅ | ❌ | ✅ |
| Request tracing | ✅ | ✅ | ✅ |
| Execution ID tracking | ✅ | ❌ | ✅ |
| Sensitive parameter obfuscation | ✅ | ❌ | ❌ |
NpgsqlRest uses Serilog with multiple output targets: console, file (with rotation), PostgreSQL table, and OpenTelemetry for distributed tracing.
Configuration Approach
NpgsqlRest: SQL Comments
-- Define endpoint with custom path and authentication
create function api.get_user(p_id int)
returns json as $$
select to_json(u) from users u where id = p_id;
$$ language sql;
comment on function api.get_user is '
HTTP GET /users/{p_id}
@authorize admin, user
@cached
@cache_expires_in 300
@rate_limiter_policy standard
';Everything is configured through SQL comments directly on your functions and tables. This keeps API configuration close to the implementation and version-controlled with your schema.
This is a unique NpgsqlRest feature: every endpoint can be individually configured with its own caching policy, rate limiting, authentication requirements, timeout, retry strategy, and more—all stored in the database alongside your code. PostgREST and Supabase apply configuration globally or require external tools for per-endpoint customization.
PostgREST: External Configuration + RLS
-- PostgREST relies on Row Level Security
create policy "Users can view own data"
on users for select
using (auth.uid() = id);PostgREST uses a combination of configuration files and PostgreSQL Row Level Security policies. Custom paths and advanced routing require an API gateway like Kong.
Supabase: Dashboard + RLS + Edge Functions
Supabase uses a web dashboard for most configuration, RLS for authorization, and Edge Functions (Deno) for custom logic. More moving parts, but provides a visual interface.
Deployment Comparison
NpgsqlRest
# Option 1: Direct download (30MB)
wget https://github.com/NpgsqlRest/NpgsqlRest/releases/latest/download/npgsqlrest-linux64
chmod +x npgsqlrest-linux64
./npgsqlrest-linux64 --connection "Host=localhost;Database=mydb;Username=api"
# Option 2: Docker
docker run -p 8080:8080 vbilopav/npgsqlrest:latest \
--connection "Host=host.docker.internal;Database=mydb;Username=api"
# Option 3: NPM
npm install -g npgsqlrest
npx npgsqlrest --connection "..."Single binary, zero dependencies. Works on Windows, Linux (x64/ARM64), and macOS.
PostgREST
# Download and run
wget https://github.com/PostgREST/postgrest/releases/latest/download/postgrest-linux-static-x64.tar.xz
tar xf postgrest-linux-static-x64.tar.xz
./postgrest postgrest.conf
# Docker
docker run -p 3000:3000 postgrest/postgrestSimilar simplicity, but requires external services for authentication and file handling.
Supabase Self-Hosted
# Clone the Docker setup
git clone https://github.com/supabase/supabase
cd supabase/docker
cp .env.example .env
docker compose up -dSupabase self-hosting requires Docker Compose with 7+ containers: PostgreSQL, PostgREST, GoTrue, Realtime, Storage, Kong, Studio, and more. More complex to maintain and scale.
When to Choose Each
Choose NpgsqlRest When:
- Performance is critical - 6x faster than PostgREST under load
- You want function-first API design - Business logic in PostgreSQL functions, not client-side query composition
- You need enterprise features - Caching (memory/Redis/hybrid), rate limiting, retry logic, multi-host failover
- You need file handling - Upload images, process CSV/Excel, store as Large Objects
- You want real-time without WebSocket complexity - SSE with PostgreSQL RAISE statements
- You prefer simple deployment - Single binary, no orchestration
- You use TypeScript - Auto-generated type-safe clients
- You want custom URL paths -
/users/{id}instead of/rpc/get_user?id=1
Choose PostgREST When:
- You want a proven, mature solution - PostgREST has been around since 2014
- You need flexible client-side queries - Resource embedding, filtering, aggregates, pagination
- You want GraphQL-like query composition - Clients can request exactly the data they need
- You're already using Row Level Security - PostgREST integrates well with RLS
- Your API is table/view-centric - Most of your endpoints map directly to tables
- You prefer Haskell's correctness guarantees - PostgREST is written in Haskell
Choose Supabase When:
- You want a complete platform - Auth, storage, realtime, edge functions, dashboard
- You prefer managed hosting - Let Supabase handle infrastructure
- You need the Studio UI - Visual database management and API exploration
- You want social login out of the box - Google, GitHub, etc. pre-configured
- Your team is less experienced with PostgreSQL - More guardrails and documentation
- You're building a prototype quickly - Fastest time-to-market for simple apps
Migration Considerations
From PostgREST to NpgsqlRest
- Functions work identically - Both expose PostgreSQL functions as endpoints
- Add SQL comments for configuration - Replace external config with inline annotations
- Replace RLS with function-level auth - Or keep RLS and add
authorizeannotations - Gain features - Caching, rate limiting, file uploads now available
From Supabase to NpgsqlRest
- Keep your PostgreSQL schema - It's still just PostgreSQL
- Replace GoTrue with built-in auth - JWT, Cookie, or Basic Auth
- Replace Storage with NpgsqlRest uploads - File system or Large Objects
- Replace Realtime with SSE - Simpler protocol, works through standard HTTP
- Move Edge Functions to PostgreSQL - Or use reverse proxy for external calls
Conclusion
| Criteria | Winner |
|---|---|
| Raw Performance | NpgsqlRest (6x faster) |
| Table/View Query Flexibility | PostgREST / Supabase |
| Function-Based APIs | NpgsqlRest |
| Per-Endpoint Configuration | NpgsqlRest |
| Deployment Simplicity | NpgsqlRest / PostgREST (tie) |
| Authentication Options | NpgsqlRest |
| File Handling | NpgsqlRest |
| Enterprise Features (caching, rate limiting) | NpgsqlRest |
| Custom Types / Nested JSON | All three (different strengths) |
| Real-Time | Supabase (WebSockets) / NpgsqlRest (SSE) |
| Managed Hosting | Supabase |
| Visual Dashboard | Supabase |
| Maturity/Community | PostgREST / Supabase |
Each tool excels in different areas:
NpgsqlRest is ideal when you need enterprise-grade features (caching, rate limiting, multi-host failover, retry logic), high performance, custom URL paths, file handling, or prefer keeping your API logic in PostgreSQL functions. A unique strength is per-endpoint configuration stored in the database—each function can have its own caching, rate limiting, timeout, and authentication rules defined via SQL comments, version-controlled alongside your schema. The function-first approach gives you explicit control over every query.
PostgREST shines for flexible client-side queries with its GraphQL-like resource embedding, 28+ filtering operators, aggregates, and pagination. It's the right choice when your API consumers need to compose their own queries against tables and views.
Supabase is ideal when you want a complete platform rather than a tool - especially for teams that prefer managed services, visual interfaces, and need auth, storage, and realtime out of the box.
More Blog Posts:
Custom Types & Multiset · Performance & High Availability · Benchmark 2025 · End-to-End Type Checking · Database-Level Security · Multiple Auth Schemes & RBAC · PostgreSQL BI Server · Secure Image Uploads · CSV & Excel Ingestion · Real-Time Chat with SSE · External API Calls · Reverse Proxy & AI Service · Zero to CRUD API
Get Started:
Installation Guide · Quick Start · Authentication Configuration · File Upload Configuration