Skip to content
Calculating...

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 /users
  • POST /orders
  • PATCH /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:

yaml
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:
      - db

Environment Variables Explained

Database Connection

yaml
PGRST_DB_URI

Connection string to PostgreSQL.

Example:

text
postgres://app_user:strongpassword@db:5432/company_app

Format:

text
postgres://USER:PASSWORD@HOST:PORT/DATABASE

Anonymous Role

yaml
PGRST_DB_ANON_ROLE

Default PostgreSQL role used when requests are unauthenticated.

Example:

yaml
PGRST_DB_ANON_ROLE: readonly_user

This role must exist in PostgreSQL.


Exposed Schemas

yaml
PGRST_DB_SCHEMAS

Defines which schemas PostgREST can expose.

Example:

yaml
PGRST_DB_SCHEMAS: public

Multiple schemas:

yaml
PGRST_DB_SCHEMAS: public,api,analytics

Query Planning

yaml
PGRST_DB_PLAN_ENABLED

Enables execution plan inspection for debugging query performance.

yaml
"true"

Optional but useful in development.


Aggregates

yaml
PGRST_DB_AGGREGATES_ENABLED

Allows aggregate queries like:

  • count
  • sum
  • avg
  • min
  • max

Example:

yaml
"true"

JWT Authentication

yaml
PGRST_JWT_SECRET

Secret used to validate JWT tokens.

If your frontend/backend generates JWTs, PostgREST can validate them and map claims to PostgreSQL roles.

Example:

yaml
PGRST_JWT_SECRET: very_long_secure_secret

Available APIs

Suppose you have this table:

sql
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

http
GET /users

Example:

bash
curl http://localhost:5000/users

Get One Record

http
GET /users?id=eq.<uuid>

Example:

bash
curl "http://localhost:5000/users?id=eq.123"

Insert

http
POST /users

Example:

bash
curl http://localhost:5000/users \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Alice",
    "email": "alice@example.com"
  }'

Update

http
PATCH /users?id=eq.<uuid>

Example:

bash
curl http://localhost:5000/users?id=eq.123 \
  -X PATCH \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Alice Smith"
  }'

Delete

http
DELETE /users?id=eq.<uuid>

Example:

bash
curl http://localhost:5000/users?id=eq.123 \
  -X DELETE

Filtering

PostgREST supports filtering directly in query parameters.

Equal:

http
/users?name=eq.Alice

Not equal:

http
/users?name=neq.Alice

Greater than:

http
/orders?total=gt.100

Less than:

http
/orders?total=lt.500

IN list:

http
/users?id=in.(1,2,3)

LIKE:

http
/users?name=like.*Ali*

Sorting

Ascending:

http
/users?order=name.asc

Descending:

http
/users?order=created_at.desc

Pagination

Limit:

http
/users?limit=20

Offset:

http
/users?offset=40

Combined:

http
/users?limit=20&offset=40

Selecting Specific Columns

Instead of returning all columns:

http
/users

Select specific fields:

http
/users?select=id,name

Relationships / Joins

If foreign keys exist:

sql
location_id UUID REFERENCES locations(id)

You can fetch related data:

http
/orders?select=*,locations(*)

This behaves similarly to SQL joins.

Stored Procedures (RPC)

Functions can be exposed as endpoints.

Example SQL:

sql
CREATE FUNCTION me()
RETURNS TABLE (
  id UUID,
  name TEXT
)
AS $$
  SELECT id, name
  FROM users
$$ LANGUAGE sql;

Available at:

http
POST /rpc/me

Example:

bash
curl http://localhost:5000/rpc/me -X POST

Authentication Example

Authenticated request:

bash
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