PostgREST
PostgREST is a lightweight web server that automatically turns your PostgreSQL database into a fully functional REST API.
Instead of manually building backend CRUD endpoints like:
GET /usersPOST /ordersPATCH /products/:id
PostgREST generates them directly from your database schema. PostgREST works by inspecting your PostgreSQL schema and exposing tables, views, and stored procedures as HTTP endpoints.
Why Use PostgREST?
Without PostgREST, building a backend usually means:
- creating a web server
- defining routes
- writing SQL queries manually
- validating permissions
- handling serialization
- maintaining CRUD logic
With PostgREST:
- your database becomes your backend
- schema changes automatically reflect in APIs
- filtering, pagination, sorting, and joins come built-in
- PostgreSQL roles can directly control API access
- no custom CRUD boilerplate required
It dramatically reduces backend complexity.
Docker Setup
A minimal Docker Compose setup:
services:
db:
image: postgres:16
container_name: postgres_db
restart: always
environment:
POSTGRES_USER: app_user
POSTGRES_PASSWORD: strongpassword
POSTGRES_DB: company_app
ports:
- "5432:5432"
postgrest:
image: postgrest/postgrest:v14.1
container_name: postgrest_api
restart: always
environment:
PGRST_DB_URI: postgres://app_user:strongpassword@db:5432/company_app?sslmode=disable
PGRST_DB_ANON_ROLE: app_user
PGRST_DB_SCHEMAS: public
PGRST_DB_PLAN_ENABLED: "true"
PGRST_DB_AGGREGATES_ENABLED: "true"
PGRST_JWT_SECRET: your_super_long_secret_key_here
PGRST_JWT_SECRET_IS_BASE64: "false"
ports:
- "5000:3000"
depends_on:
- dbEnvironment Variables Explained
Database Connection
PGRST_DB_URIConnection string to PostgreSQL.
Example:
postgres://app_user:strongpassword@db:5432/company_appFormat:
postgres://USER:PASSWORD@HOST:PORT/DATABASEAnonymous Role
PGRST_DB_ANON_ROLEDefault PostgreSQL role used when requests are unauthenticated.
Example:
PGRST_DB_ANON_ROLE: readonly_userThis role must exist in PostgreSQL.
Exposed Schemas
PGRST_DB_SCHEMASDefines which schemas PostgREST can expose.
Example:
PGRST_DB_SCHEMAS: publicMultiple schemas:
PGRST_DB_SCHEMAS: public,api,analyticsQuery Planning
PGRST_DB_PLAN_ENABLEDEnables execution plan inspection for debugging query performance.
"true"Optional but useful in development.
Aggregates
PGRST_DB_AGGREGATES_ENABLEDAllows aggregate queries like:
- count
- sum
- avg
- min
- max
Example:
"true"JWT Authentication
PGRST_JWT_SECRETSecret used to validate JWT tokens.
If your frontend/backend generates JWTs, PostgREST can validate them and map claims to PostgreSQL roles.
Example:
PGRST_JWT_SECRET: very_long_secure_secretAvailable APIs
Suppose you have this table:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);PostgREST automatically exposes: -->
Get All Records
GET /usersExample:
curl http://localhost:5000/usersGet One Record
GET /users?id=eq.<uuid>Example:
curl "http://localhost:5000/users?id=eq.123"Insert
POST /usersExample:
curl http://localhost:5000/users \
-X POST \
-H "Content-Type: application/json" \
-d '{
"name": "Alice",
"email": "alice@example.com"
}'Update
PATCH /users?id=eq.<uuid>Example:
curl http://localhost:5000/users?id=eq.123 \
-X PATCH \
-H "Content-Type: application/json" \
-d '{
"name": "Alice Smith"
}'Delete
DELETE /users?id=eq.<uuid>Example:
curl http://localhost:5000/users?id=eq.123 \
-X DELETEFiltering
PostgREST supports filtering directly in query parameters.
Equal:
/users?name=eq.AliceNot equal:
/users?name=neq.AliceGreater than:
/orders?total=gt.100Less than:
/orders?total=lt.500IN list:
/users?id=in.(1,2,3)LIKE:
/users?name=like.*Ali*Sorting
Ascending:
/users?order=name.ascDescending:
/users?order=created_at.descPagination
Limit:
/users?limit=20Offset:
/users?offset=40Combined:
/users?limit=20&offset=40Selecting Specific Columns
Instead of returning all columns:
/usersSelect specific fields:
/users?select=id,nameRelationships / Joins
If foreign keys exist:
location_id UUID REFERENCES locations(id)You can fetch related data:
/orders?select=*,locations(*)This behaves similarly to SQL joins.
Stored Procedures (RPC)
Functions can be exposed as endpoints.
Example SQL:
CREATE FUNCTION me()
RETURNS TABLE (
id UUID,
name TEXT
)
AS $$
SELECT id, name
FROM users
$$ LANGUAGE sql;Available at:
POST /rpc/meExample:
curl http://localhost:5000/rpc/me -X POSTAuthentication Example
Authenticated request:
curl http://localhost:5000/users \
-H "Authorization: Bearer YOUR_JWT_TOKEN"PostgREST validates the token and applies PostgreSQL permissions.
Best Practices
- never expose PostgreSQL superusers
- create dedicated API roles
- restrict anonymous access
- use JWT authentication
- expose only required schemas
- use views for safe public APIs
- move complex business logic into SQL functions
- rely on PostgreSQL permissions instead of app-side hacks
