The REST API — PostgREST
The idea in one sentence
Section titled “The idea in one sentence”PostgREST reads your Postgres schema and turns every exposed table and view into a REST endpoint automatically — you never hand-write API route code.
From a table to an endpoint
Section titled “From a table to an endpoint”When you create a table public.movies, you do not write a controller, a route, or a serializer for it. PostgREST connects to your database, introspects the schema (tables, columns, types, foreign keys), and generates a matching REST endpoint at request time. The table becomes reachable at:
GET /rest/v1/moviesThis is the mechanism behind the Data API in every Supabase project — PostgREST sits in front of Postgres and translates HTTP requests into SQL queries. Add a column, and the response shape changes on the next request. Add a table, and a new endpoint exists — no deploy step, no code to write.
create table public.movies ( id bigint generated always as identity primary key, title text not null, year int not null, director_id bigint references public.directors (id));Filtering, selecting, and ordering with query parameters
Section titled “Filtering, selecting, and ordering with query parameters”PostgREST reads plain URL query parameters and turns them into a SQL where, select, and order by. Choose which columns come back with select:
GET /rest/v1/movies?select=id,title,yearFilter rows using PostgREST’s column=operator.value syntax. Common operators are eq. (equals), gt./gte. (greater than / or equal), lt./lte., like. (pattern match), and in. (membership in a list):
GET /rest/v1/movies?year=gte.2020GET /rest/v1/movies?title=like.*Matrix*GET /rest/v1/movies?year=in.(2020,2021,2022)Sort the result with order, and mark descending with .desc:
GET /rest/v1/movies?order=year.descAll three combine in a single request:
GET /rest/v1/movies?select=id,title,year&year=gte.2020&order=year.descEmbedding related resources through foreign keys
Section titled “Embedding related resources through foreign keys”PostgREST can follow a foreign-key constraint and return the related row nested inside the response, in one round trip — no separate query, no manual join in your application code. Because public.movies.director_id references public.directors.id, you can ask for the director alongside each movie:
GET /rest/v1/movies?select=title,director:directors(name)This returns each movie with a nested director object containing name. PostgREST works this out by reading the actual foreign-key constraint in your schema — there is no separate relationship-mapping file to maintain. If the constraint changes or is dropped, the embedding stops working, because the schema is the single source of truth.
As a related fact worth knowing: the same schema is also exposed automatically as a GraphQL API through the pg_graphql extension, for callers who prefer GraphQL over REST. This course focuses on the REST/RPC surface, but the GraphQL endpoint exists side by side with no extra schema work.
flowchart LR
tbl[("public.movies (with FK to directors)")] --> introspect["PostgREST introspects the schema"]
introspect --> endpoint["GET /rest/v1/movies"]
endpoint --> select["?select= chooses columns"]
endpoint --> filter["?year=gte.2020 filters rows"]
endpoint --> order["?order=year.desc sorts rows"]
endpoint --> embed["?select=title,director:directors(name) embeds via FK"]