Relational DB to RESTful API
Taking the database seriously
Most web frameworks treat
the DB as a dumb store
This helps give them broad
appeal
What if we take a stand
instead?
??
? ?
?
Taking the database seriously
Is PostgreSQL powerful and
flexible enough to replace
the custom API server?
That’s my experiment
The Traditional Web API Stack
Your App
Web Server
Database
The Traditional Web API Stack
Your App
Web Server
Database
PostgREST
A no-configuration canonical
mapping from DB to HTTP
Talk Overview
The Traditional API Server (brief)
Live demo of PostgREST
What’s the SQL? How did
it do that?
The Traditional App
Handmade Nested
Routes
Controllers
Imperative code
ORM
Logic divorced from
data
What’s in an app?
HTTP request handling
Authentication
Authorization
Request Parsing
Request Validation
Database Communication
Database Response Handling
HTTP Response Building
With error handling woven
throughout...
Maintaining bespoke APIs gets old
“Most APIs look the
same, some have
icing, some have
fondant, some are
vanilla, some
chocolate. At the
core they’re all still
cakes.” -- Jett
Durham
Problem 1: Boilerplate
Want to add a new route?
Create model
Add each CRUD action
Check permissions
Support filtering, pagination
Special routes for joining data
Problem 2: No Single Source of Truth
Constraints are removed
from DB
No longer enforced
continuously + uniformly
Imperative code means
human must write docs
Authorization is per-
controller rather than
Problem 3: Hierarchy
Your info is relational, your routes
hierarchical
Say projects have parts and vice
versa.
Need routes for parts by project
and project by parts?
Other people recognize the
problem, hence GraphQL
Demo Time!
We’ll use the Pagila example database
It was ported from MySQL “Sakila”
It’s a DVD store with films, rentals, customers, payments,
categories, actors etc
A Tour of PostgREST
Security - Roles for Authorization
Anonymous Authenticator User(s)
Security - JWT for Authentication
YES
NO
Security - Roles in SQL
CREATE ROLE authenticator NOINHERIT LOGIN;
CREATE ROLE anon;
CREATE ROLE worker;
GRANT anon, worker TO authenticator;
Switching to a role
BEGIN ISOLATION LEVEL READ COMMITTED READ WRITE;
SET LOCAL ROLE 'worker';
SET LOCAL "postgrest.claims.id" = 'jdoe';
-- ...
COMMIT;
Row-Level Security
PostgreSQL 9.5+ allows restricting access to individual rows
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
drop policy if exists authors_eigenedit on posts;
create policy authors_eigenedit on posts
using (true)
with check (
author = basic_auth.current_email()
);
Let’s see it in action
External Actions
You can’t do everything
inside SQL
How do you
● Send an email?
● Call a 3rd party
service?
LISTEN / NOTIFY
How to version the API?
So far OK but… but I don’t
want to couple the internal
schema with an API!
How to encapsulate true
schema?
How to version specific
endpoints?
Use database schemas
Internal Schema V1
table1
table2
table3
view2
proc
view2
HTTP Interface is Flexible
postgrest --schema v1
postgrest --schema v2,v1
postgrest --schema v3,v2,v1
Accept: application/json;
version=2
OR
GET /v2/...
Use the schema search-path
SET search_path TO v2, v1;
How does it work inside?
Warning: Boring / Cool
Generating the payload in 100% SQL
WITH pg_source AS
(SELECT "public"."festival".* FROM "public"."festival")
SELECT
(SELECT pg_catalog.count(1) FROM "public"."festival") AS total_result_set,
pg_catalog.count(t) AS page_total,
NULL AS header,
array_to_json(array_agg(row_to_json(t)))::character VARYING AS body
FROM
(SELECT * FROM pg_source LIMIT ALL OFFSET 0) t
Adding a filter
WITH pg_source AS
(SELECT "public"."festival".* FROM "public"."festival"
WHERE "public"."festival"."name" LIKE '%fun%'::UNKNOWN)
SELECT
(SELECT pg_catalog.count(1) FROM "public"."festival"
WHERE "public"."festival"."name" LIKE '%fun%'::UNKNOWN) AS total_result_set,
pg_catalog.count(t) AS page_total,
NULL AS header,
array_to_json(array_agg(row_to_json(t)))::character varying AS body
FROM
(SELECT * FROM pg_source LIMIT ALL OFFSET 0) t
Optimistic cast
Or without a global count
WITH pg_source AS
(SELECT "public"."festival".* FROM "public"."festival")
SELECT
NULL AS total_result_set,
pg_catalog.count(t) AS page_total,
NULL AS header,
array_to_json(array_agg(row_to_json(t)))::character varying AS body
FROM
(SELECT * FROM pg_source LIMIT ALL OFFSET 0) t
Creating CSV body
-- ...
(SELECT string_agg(a.k, ',')
FROM
(SELECT json_object_keys(r)::TEXT AS k
FROM
(SELECT row_to_json(hh) AS r
FROM pg_source AS hh LIMIT 1) s
) a
) || 'n' ||
coalesce(
string_agg(
substring(t::text, 2, length(t::text) - 2), 'n'
), ''
)
-- ...
First row
Column names
Remove quotes
Embedding a relation
WITH pg_source AS
(SELECT "public"."film"."id", row_to_json("director".*) AS "director"
FROM "public"."film"
LEFT OUTER JOIN
(SELECT "public"."director".*
FROM "public"."director") AS "director"
ON "director"."name" = "film"."director")
SELECT
(SELECT pg_catalog.count(1) FROM "public"."film") AS total_result_set,
pg_catalog.count(t) AS page_total,
NULL AS header,
array_to_json(array_agg(row_to_json(t)))::character varying AS body
FROM
(SELECT * FROM pg_source LIMIT ALL OFFSET 0) t
Embed row as field
Key(s) detected
ELSE
CASE
WHEN t.typelem <> 0::oid AND t.typlen = (-1)
THEN 'ARRAY'::text
WHEN nt.nspname = 'pg_catalog'::name THEN
format_type(a.atttypid, NULL::integer)
ELSE 'USER-DEFINED'::text
END
END::information_schema.character_data AS data_type,
information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*,
t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS character_maximum_length,
information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.
*, t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS character_octet_length,
information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.
*, t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS numeric_precision,
information_schema._pg_numeric_precision_radix(information_schema._pg_truety
pid(a.*, t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS numeric_precision_radix,
information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*,
t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS numeric_scale,
information_schema._pg_datetime_precision(information_schema._pg_truetypid(a
.*, t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS datetime_precision,
information_schema._pg_interval_type(information_schema._pg_truetypid(a.*,
t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.character_data AS interval_type,
NULL::integer::information_schema.cardinal_number AS
interval_precision,
NULL::character varying::information_schema.sql_identifier
AS character_set_catalog,
NULL::character varying::information_schema.sql_identifier
AS character_set_schema,
NULL::character varying::information_schema.sql_identifier
AS character_set_name,
SELECT DISTINCT
info.table_schema AS schema,
info.table_name AS table_name,
info.column_name AS name,
info.ordinal_position AS position,
info.is_nullable::boolean AS nullable,
info.data_type AS col_type,
info.is_updatable::boolean AS updatable,
info.character_maximum_length AS max_len,
info.numeric_precision AS precision,
info.column_default AS default_value,
array_to_string(enum_info.vals, ',') AS enum
FROM (
/*
-- CTE based on information_schema.columns to remove the owner filter
*/
WITH columns AS (
SELECT current_database()::information_schema.sql_identifier AS table_catalog,
nc.nspname::information_schema.sql_identifier AS table_schema,
c.relname::information_schema.sql_identifier AS table_name,
a.attname::information_schema.sql_identifier AS column_name,
a.attnum::information_schema.cardinal_number AS ordinal_position,
pg_get_expr(ad.adbin, ad.adrelid)::information_schema.character_data AS column_default,
CASE
WHEN a.attnotnull OR t.typtype = 'd'::"char" AND t.typnotnull THEN 'NO'::text
ELSE 'YES'::text
END::information_schema.yes_or_no AS is_nullable,
CASE
WHEN t.typtype = 'd'::"char" THEN
CASE
WHEN bt.typelem <> 0::oid AND bt.typlen = (-1) THEN 'ARRAY'::text
WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(t.typbasetype,
NULL::integer)
ELSE 'USER-DEFINED'::text
END
Matching up foreign keys
Deleting an item
WITH pg_source AS
(DELETE FROM "test"."items"
WHERE "test"."items"."id" = '1'::unknown
RETURNING "test"."items".*)
SELECT
'' AS total_result_set,
pg_catalog.count(t) AS page_total,
'',
''
FROM
(SELECT 1 FROM pg_source) t
Learning More
Read the Docs
http://postgrest.com
github.com / begriffs / postgrest

More Related Content

ODP
Postgrest: the REST API for PostgreSQL databases
PDF
Introduction to container based virtualization with docker
PDF
A Introduction of Packer
PPTX
Mongo db
PPTX
Ansible presentation
ODP
Basics of VueJS
PPT
Containers 101
PPTX
Introduction to Angularjs
Postgrest: the REST API for PostgreSQL databases
Introduction to container based virtualization with docker
A Introduction of Packer
Mongo db
Ansible presentation
Basics of VueJS
Containers 101
Introduction to Angularjs

What's hot (20)

PPTX
Docker 101 : Introduction to Docker and Containers
PPTX
Docker Tutorial For Beginners | What Is Docker And How It Works? | Docker Tut...
PPT
Chapter 5 slides
PDF
HDFS Design Principles
PDF
[WhaTap DevOps Day] 세션 6 : 와탭랩스 DevOps 이야기
PPTX
Architetture a Microservizi con Docker Container
ODP
Introduction To RabbitMQ
PPTX
Monitoring With Prometheus
PPTX
What Is A Docker Container? | Docker Container Tutorial For Beginners| Docker...
PPTX
Containerization & Docker - Under the Hood
DOCX
Cloud operating systems
PDF
Introduction to Docker storage, volume and image
PPTX
The RabbitMQ Message Broker
PDF
Building layers of defense for your application
PPTX
Apache Zookeeper Explained: Tutorial, Use Cases and Zookeeper Java API Examples
PDF
Docker, Linux Containers (LXC), and security
PPT
presentation on Docker
PPT
Backdoor
PPTX
Python/Flask Presentation
ODP
Introduction to Ansible
Docker 101 : Introduction to Docker and Containers
Docker Tutorial For Beginners | What Is Docker And How It Works? | Docker Tut...
Chapter 5 slides
HDFS Design Principles
[WhaTap DevOps Day] 세션 6 : 와탭랩스 DevOps 이야기
Architetture a Microservizi con Docker Container
Introduction To RabbitMQ
Monitoring With Prometheus
What Is A Docker Container? | Docker Container Tutorial For Beginners| Docker...
Containerization & Docker - Under the Hood
Cloud operating systems
Introduction to Docker storage, volume and image
The RabbitMQ Message Broker
Building layers of defense for your application
Apache Zookeeper Explained: Tutorial, Use Cases and Zookeeper Java API Examples
Docker, Linux Containers (LXC), and security
presentation on Docker
Backdoor
Python/Flask Presentation
Introduction to Ansible
Ad

Similar to A Tour of PostgREST (20)

PPTX
SQL Server - Introduction to TSQL
PDF
9 Python programming notes for ktu physics and computer application semester 4
PDF
Expanding your impact with programmability in the data center
PPT
닷넷 개발자를 위한 패턴이야기
PDF
Seattle StrongLoop Node.js Workshop
PPT
PPT
The 90-Day Startup with Google AppEngine for Java
ODP
Plproxy
PPT
OWB11gR2 - Extending ETL
PDF
Python RESTful webservices with Python: Flask and Django solutions
ODP
Porting Applications From Oracle To PostgreSQL
PDF
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
PDF
[db tech showcase Tokyo 2017] C23: Lessons from SQLite4 by SQLite.org - Richa...
PDF
Engage 2023: Taking Domino Apps to the next level by providing a Rest API
PPTX
Javazone 2010-lift-framework-public
PDF
Choisir entre une API RPC, SOAP, REST, GraphQL? 
Et si le problème était ai...
PDF
Simon Elliston Ball – When to NoSQL and When to Know SQL - NoSQL matters Barc...
PDF
Postgres Vienna DB Meetup 2014
SQL Server - Introduction to TSQL
9 Python programming notes for ktu physics and computer application semester 4
Expanding your impact with programmability in the data center
닷넷 개발자를 위한 패턴이야기
Seattle StrongLoop Node.js Workshop
The 90-Day Startup with Google AppEngine for Java
Plproxy
OWB11gR2 - Extending ETL
Python RESTful webservices with Python: Flask and Django solutions
Porting Applications From Oracle To PostgreSQL
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
[db tech showcase Tokyo 2017] C23: Lessons from SQLite4 by SQLite.org - Richa...
Engage 2023: Taking Domino Apps to the next level by providing a Rest API
Javazone 2010-lift-framework-public
Choisir entre une API RPC, SOAP, REST, GraphQL? 
Et si le problème était ai...
Simon Elliston Ball – When to NoSQL and When to Know SQL - NoSQL matters Barc...
Postgres Vienna DB Meetup 2014
Ad

Recently uploaded (20)

PDF
Dell Pro Micro: Speed customer interactions, patient processing, and learning...
PDF
A symptom-driven medical diagnosis support model based on machine learning te...
PDF
Electrocardiogram sequences data analytics and classification using unsupervi...
PDF
AI.gov: A Trojan Horse in the Age of Artificial Intelligence
PDF
The-Future-of-Automotive-Quality-is-Here-AI-Driven-Engineering.pdf
PDF
Transform-Your-Factory-with-AI-Driven-Quality-Engineering.pdf
PDF
Comparative analysis of machine learning models for fake news detection in so...
PDF
The-2025-Engineering-Revolution-AI-Quality-and-DevOps-Convergence.pdf
PPTX
Internet of Everything -Basic concepts details
PPTX
future_of_ai_comprehensive_20250822032121.pptx
PPTX
GROUP4NURSINGINFORMATICSREPORT-2 PRESENTATION
PPTX
SGT Report The Beast Plan and Cyberphysical Systems of Control
PDF
giants, standing on the shoulders of - by Daniel Stenberg
PDF
Lung cancer patients survival prediction using outlier detection and optimize...
PPTX
Training Program for knowledge in solar cell and solar industry
PDF
NewMind AI Weekly Chronicles – August ’25 Week IV
PDF
4 layer Arch & Reference Arch of IoT.pdf
PDF
Rapid Prototyping: A lecture on prototyping techniques for interface design
PDF
MENA-ECEONOMIC-CONTEXT-VC MENA-ECEONOMIC
PPTX
AI-driven Assurance Across Your End-to-end Network With ThousandEyes
Dell Pro Micro: Speed customer interactions, patient processing, and learning...
A symptom-driven medical diagnosis support model based on machine learning te...
Electrocardiogram sequences data analytics and classification using unsupervi...
AI.gov: A Trojan Horse in the Age of Artificial Intelligence
The-Future-of-Automotive-Quality-is-Here-AI-Driven-Engineering.pdf
Transform-Your-Factory-with-AI-Driven-Quality-Engineering.pdf
Comparative analysis of machine learning models for fake news detection in so...
The-2025-Engineering-Revolution-AI-Quality-and-DevOps-Convergence.pdf
Internet of Everything -Basic concepts details
future_of_ai_comprehensive_20250822032121.pptx
GROUP4NURSINGINFORMATICSREPORT-2 PRESENTATION
SGT Report The Beast Plan and Cyberphysical Systems of Control
giants, standing on the shoulders of - by Daniel Stenberg
Lung cancer patients survival prediction using outlier detection and optimize...
Training Program for knowledge in solar cell and solar industry
NewMind AI Weekly Chronicles – August ’25 Week IV
4 layer Arch & Reference Arch of IoT.pdf
Rapid Prototyping: A lecture on prototyping techniques for interface design
MENA-ECEONOMIC-CONTEXT-VC MENA-ECEONOMIC
AI-driven Assurance Across Your End-to-end Network With ThousandEyes

A Tour of PostgREST

  • 1. Relational DB to RESTful API
  • 2. Taking the database seriously Most web frameworks treat the DB as a dumb store This helps give them broad appeal What if we take a stand instead? ?? ? ? ?
  • 3. Taking the database seriously Is PostgreSQL powerful and flexible enough to replace the custom API server? That’s my experiment
  • 4. The Traditional Web API Stack Your App Web Server Database
  • 5. The Traditional Web API Stack Your App Web Server Database PostgREST A no-configuration canonical mapping from DB to HTTP
  • 6. Talk Overview The Traditional API Server (brief) Live demo of PostgREST What’s the SQL? How did it do that?
  • 7. The Traditional App Handmade Nested Routes Controllers Imperative code ORM Logic divorced from data
  • 8. What’s in an app? HTTP request handling Authentication Authorization Request Parsing Request Validation Database Communication Database Response Handling HTTP Response Building With error handling woven throughout...
  • 9. Maintaining bespoke APIs gets old “Most APIs look the same, some have icing, some have fondant, some are vanilla, some chocolate. At the core they’re all still cakes.” -- Jett Durham
  • 10. Problem 1: Boilerplate Want to add a new route? Create model Add each CRUD action Check permissions Support filtering, pagination Special routes for joining data
  • 11. Problem 2: No Single Source of Truth Constraints are removed from DB No longer enforced continuously + uniformly Imperative code means human must write docs Authorization is per- controller rather than
  • 12. Problem 3: Hierarchy Your info is relational, your routes hierarchical Say projects have parts and vice versa. Need routes for parts by project and project by parts? Other people recognize the problem, hence GraphQL
  • 13. Demo Time! We’ll use the Pagila example database It was ported from MySQL “Sakila” It’s a DVD store with films, rentals, customers, payments, categories, actors etc
  • 15. Security - Roles for Authorization Anonymous Authenticator User(s)
  • 16. Security - JWT for Authentication YES NO
  • 17. Security - Roles in SQL CREATE ROLE authenticator NOINHERIT LOGIN; CREATE ROLE anon; CREATE ROLE worker; GRANT anon, worker TO authenticator;
  • 18. Switching to a role BEGIN ISOLATION LEVEL READ COMMITTED READ WRITE; SET LOCAL ROLE 'worker'; SET LOCAL "postgrest.claims.id" = 'jdoe'; -- ... COMMIT;
  • 19. Row-Level Security PostgreSQL 9.5+ allows restricting access to individual rows ALTER TABLE posts ENABLE ROW LEVEL SECURITY; drop policy if exists authors_eigenedit on posts; create policy authors_eigenedit on posts using (true) with check ( author = basic_auth.current_email() );
  • 20. Let’s see it in action
  • 21. External Actions You can’t do everything inside SQL How do you ● Send an email? ● Call a 3rd party service? LISTEN / NOTIFY
  • 22. How to version the API? So far OK but… but I don’t want to couple the internal schema with an API! How to encapsulate true schema? How to version specific endpoints?
  • 23. Use database schemas Internal Schema V1 table1 table2 table3 view2 proc view2
  • 24. HTTP Interface is Flexible postgrest --schema v1 postgrest --schema v2,v1 postgrest --schema v3,v2,v1 Accept: application/json; version=2 OR GET /v2/...
  • 25. Use the schema search-path SET search_path TO v2, v1;
  • 26. How does it work inside? Warning: Boring / Cool
  • 27. Generating the payload in 100% SQL WITH pg_source AS (SELECT "public"."festival".* FROM "public"."festival") SELECT (SELECT pg_catalog.count(1) FROM "public"."festival") AS total_result_set, pg_catalog.count(t) AS page_total, NULL AS header, array_to_json(array_agg(row_to_json(t)))::character VARYING AS body FROM (SELECT * FROM pg_source LIMIT ALL OFFSET 0) t
  • 28. Adding a filter WITH pg_source AS (SELECT "public"."festival".* FROM "public"."festival" WHERE "public"."festival"."name" LIKE '%fun%'::UNKNOWN) SELECT (SELECT pg_catalog.count(1) FROM "public"."festival" WHERE "public"."festival"."name" LIKE '%fun%'::UNKNOWN) AS total_result_set, pg_catalog.count(t) AS page_total, NULL AS header, array_to_json(array_agg(row_to_json(t)))::character varying AS body FROM (SELECT * FROM pg_source LIMIT ALL OFFSET 0) t Optimistic cast
  • 29. Or without a global count WITH pg_source AS (SELECT "public"."festival".* FROM "public"."festival") SELECT NULL AS total_result_set, pg_catalog.count(t) AS page_total, NULL AS header, array_to_json(array_agg(row_to_json(t)))::character varying AS body FROM (SELECT * FROM pg_source LIMIT ALL OFFSET 0) t
  • 30. Creating CSV body -- ... (SELECT string_agg(a.k, ',') FROM (SELECT json_object_keys(r)::TEXT AS k FROM (SELECT row_to_json(hh) AS r FROM pg_source AS hh LIMIT 1) s ) a ) || 'n' || coalesce( string_agg( substring(t::text, 2, length(t::text) - 2), 'n' ), '' ) -- ... First row Column names Remove quotes
  • 31. Embedding a relation WITH pg_source AS (SELECT "public"."film"."id", row_to_json("director".*) AS "director" FROM "public"."film" LEFT OUTER JOIN (SELECT "public"."director".* FROM "public"."director") AS "director" ON "director"."name" = "film"."director") SELECT (SELECT pg_catalog.count(1) FROM "public"."film") AS total_result_set, pg_catalog.count(t) AS page_total, NULL AS header, array_to_json(array_agg(row_to_json(t)))::character varying AS body FROM (SELECT * FROM pg_source LIMIT ALL OFFSET 0) t Embed row as field Key(s) detected
  • 32. ELSE CASE WHEN t.typelem <> 0::oid AND t.typlen = (-1) THEN 'ARRAY'::text WHEN nt.nspname = 'pg_catalog'::name THEN format_type(a.atttypid, NULL::integer) ELSE 'USER-DEFINED'::text END END::information_schema.character_data AS data_type, information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS character_maximum_length, information_schema._pg_char_octet_length(information_schema._pg_truetypid(a. *, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS character_octet_length, information_schema._pg_numeric_precision(information_schema._pg_truetypid(a. *, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_precision, information_schema._pg_numeric_precision_radix(information_schema._pg_truety pid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_precision_radix, information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_scale, information_schema._pg_datetime_precision(information_schema._pg_truetypid(a .*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS datetime_precision, information_schema._pg_interval_type(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.character_data AS interval_type, NULL::integer::information_schema.cardinal_number AS interval_precision, NULL::character varying::information_schema.sql_identifier AS character_set_catalog, NULL::character varying::information_schema.sql_identifier AS character_set_schema, NULL::character varying::information_schema.sql_identifier AS character_set_name, SELECT DISTINCT info.table_schema AS schema, info.table_name AS table_name, info.column_name AS name, info.ordinal_position AS position, info.is_nullable::boolean AS nullable, info.data_type AS col_type, info.is_updatable::boolean AS updatable, info.character_maximum_length AS max_len, info.numeric_precision AS precision, info.column_default AS default_value, array_to_string(enum_info.vals, ',') AS enum FROM ( /* -- CTE based on information_schema.columns to remove the owner filter */ WITH columns AS ( SELECT current_database()::information_schema.sql_identifier AS table_catalog, nc.nspname::information_schema.sql_identifier AS table_schema, c.relname::information_schema.sql_identifier AS table_name, a.attname::information_schema.sql_identifier AS column_name, a.attnum::information_schema.cardinal_number AS ordinal_position, pg_get_expr(ad.adbin, ad.adrelid)::information_schema.character_data AS column_default, CASE WHEN a.attnotnull OR t.typtype = 'd'::"char" AND t.typnotnull THEN 'NO'::text ELSE 'YES'::text END::information_schema.yes_or_no AS is_nullable, CASE WHEN t.typtype = 'd'::"char" THEN CASE WHEN bt.typelem <> 0::oid AND bt.typlen = (-1) THEN 'ARRAY'::text WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(t.typbasetype, NULL::integer) ELSE 'USER-DEFINED'::text END Matching up foreign keys
  • 33. Deleting an item WITH pg_source AS (DELETE FROM "test"."items" WHERE "test"."items"."id" = '1'::unknown RETURNING "test"."items".*) SELECT '' AS total_result_set, pg_catalog.count(t) AS page_total, '', '' FROM (SELECT 1 FROM pg_source) t
  • 34. Learning More Read the Docs http://postgrest.com
  • 35. github.com / begriffs / postgrest