diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst
index 883db8bc0..10923ab15 100644
--- a/docs/ecosystem.rst
+++ b/docs/ecosystem.rst
@@ -14,6 +14,8 @@ Community Tutorials
* `"CodeLess" backend using postgres, postgrest and oauth2 authentication with keycloak `_ -
A step-by-step tutorial for using PostgREST with KeyCloak(hosted on a managed service).
+* `How PostgreSQL triggers work when called with a PostgREST PATCH HTTP request `_ - A tutorial to see how the old and new values are set or not when doing a PATCH request to PostgREST.
+
.. _templates:
Templates
diff --git a/docs/how-tos/casting-type-to-custom-json.rst b/docs/how-tos/casting-type-to-custom-json.rst
deleted file mode 100644
index b4687bad2..000000000
--- a/docs/how-tos/casting-type-to-custom-json.rst
+++ /dev/null
@@ -1,97 +0,0 @@
-Casting a type to a custom JSON object
-======================================
-
-:author: `steve-chavez `_
-
-While using PostgREST you might have noticed that certain PostgreSQL types translate to JSON strings when you would
-have expected a JSON object or array. For example, let's see the case of `range types `_.
-
-.. code-block:: postgres
-
- -- example taken from https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-EXAMPLES
- create table reservations (
- room int
- , during tsrange
- );
-
- insert into
- reservations
- values
- (1108, tsrange('2010-01-01 14:30', '2010-01-01 15:30'));
-
-Here we have a column named **during** as a ``tsrange`` type, we would like to get it as JSON through PostgREST.
-
-.. code-block:: bash
-
- curl "http://localhost:3000/reservations"
-
-Result:
-
-.. code-block:: json
-
- [
- {
- "room":1108,
- "during":"[\"2010-01-01 14:30:00\",\"2010-01-01 15:30:00\")"
- }
- ]
-
-The **during** value is probably not the in the format you want. We get a JSON string because by default PostgreSQL casts
-the type to JSON by using its ``text`` representation. We can change this representation to a custom JSON object by `creating a CAST `_ .
-
-To do this, first we'll define the function that will do the conversion from ``tsrange`` to ``json``.
-
-.. code-block:: postgres
-
- create or replace function tsrange_to_json(tsrange) returns json as $$
- select json_build_object(
- 'lower', lower($1)
- , 'upper', upper($1)
- , 'lower_inc', lower_inc($1)
- , 'upper_inc', upper_inc($1)
- );
- $$ language sql;
-
-Using this function we'll create the CAST.
-
-.. code-block:: postgres
-
- create cast (tsrange as json) with function tsrange_to_json(tsrange) as assignment;
-
-And we'll do the request and :ref:`cast the column `.
-
-.. code-block:: bash
-
- curl "http://localhost:3000/reservations?select=room,during::json"
-
-The result now is:
-
-.. code-block:: json
-
- [
- {
- "room":1108,
- "during":{
- "lower" : "2010-01-01T14:30:00",
- "upper" : "2010-01-01T15:30:00",
- "lower_inc" : true,
- "upper_inc" : false
- }
- }
- ]
-
-You can use the same idea for creating custom casts for different types.
-
-.. note::
-
- If you don't want to modify casts for built-in types, an option would be to `create a custom type `_
- for your own ``tsrange`` and add its own cast.
-
- .. code-block:: postgres
-
- create type mytsrange as range (subtype = timestamp, subtype_diff = tsrange_subdiff);
-
- -- define column types and casting function analoguously to the above example
- -- ...
-
- create cast (mytsrange as json) with function mytsrange_to_json(mytsrange) as assignment;
diff --git a/docs/how-tos/embedding-table-from-another-schema.rst b/docs/how-tos/embedding-table-from-another-schema.rst
deleted file mode 100644
index 6631982de..000000000
--- a/docs/how-tos/embedding-table-from-another-schema.rst
+++ /dev/null
@@ -1,81 +0,0 @@
-Embedding a table from another schema
-=====================================
-
-:author: `steve-chavez `_
-
-Suppose you have a **people** table in the ``public`` schema and this schema is exposed through PostgREST's :ref:`db-schemas`.
-
-.. code-block:: postgres
-
- create table public.people(
- id int primary key
- , full_name text
- );
-
-And you want to :ref:`embed ` the **people** table with a **details** table that's in another schema named ``private``.
-
-.. code-block:: postgres
-
- create schema if not exists private;
-
- -- For simplicity's sake the table is devoid of constraints/domains on email, phone, etc.
- create table private.details(
- id int primary key references public.people
- , email text
- , phone text
- , birthday date
- , occupation text
- , company text
- );
-
- -- other database objects in this schema
- -- ...
- -- ...
-
-To solve this, you can create a view of **details** in the ``public`` schema. We'll call it **public_details**.
-
-.. code-block:: postgres
-
- create view public.public_details as
- select
- id
- , occupation
- , company
- from
- private.details;
-
-Since PostgREST supports :ref:`embedding_views`, you can embed **people** with **public_details**.
-
-Let's insert some data to test this:
-
-.. code-block:: postgres
-
- insert into
- public.people
- values
- (1, 'John Doe'), (2, 'Jane Doe');
-
- insert into
- private.details
- values
- (1, 'jhon@fake.com', '772-323-5433', '1990-02-01', 'Transportation attendant', 'Body Fate'),
- (2, 'jane@fake.com', '480-474-6571', '1980-04-21', 'Geotechnical engineer', 'Earthworks Garden Kare');
-
-.. important::
-
- Make sure PostgREST's schema cache is up-to-date. See :ref:`schema_reloading`.
-
-Now, make the following request:
-
-.. code-block:: bash
-
- curl "http://localhost:3000/people?select=full_name,public_details(occupation,company)"
-
-The result should be:
-
-.. code-block:: json
-
- [
- {"full_name":"John Doe","public_details":[{"occupation":"Transportation attendant","company":"Body Fate"}]},
- {"full_name":"Jane Doe","public_details":[{"occupation":"Geotechnical engineer","company":"Earthworks Garden Kare"}]}
- ]
diff --git a/docs/how-tos/working-with-postgresql-data-types.rst b/docs/how-tos/working-with-postgresql-data-types.rst
index 921be2ba5..06276f6c7 100644
--- a/docs/how-tos/working-with-postgresql-data-types.rst
+++ b/docs/how-tos/working-with-postgresql-data-types.rst
@@ -79,6 +79,259 @@ You can use other comparative filters and also `PostgreSQL special date/time inp
}
]
+JSON
+----
+
+To work with a ``json`` type column, you can handle the value as a JSON object. For instance, let's use this table:
+
+.. code-block:: postgres
+
+ create table products (
+ id int primary key,
+ name text unique,
+ extra_info json
+ );
+
+Now, you can insert a new product using a JSON object for the ``extra_info`` column:
+
+.. tabs::
+
+ .. code-tab:: http
+
+ POST /products HTTP/1.1
+ Content-Type: application/json
+
+ {
+ "id": 1,
+ "name": "Canned fish",
+ "extra_info": {
+ "expiry_date": "2025-12-31",
+ "exportable": true
+ }
+ }
+
+ .. code-tab:: bash Curl
+
+ curl "http://localhost:3000/products" \
+ -X POST -H "Content-Type: application/json" \
+ -d @- << EOF
+ {
+ "id": 1,
+ "name": "Canned fish",
+ "extra_info": {
+ "expiry_date": "2025-12-31",
+ "exportable": true
+ }
+ }
+ EOF
+
+To query and filter the data see :ref:`json_columns` for a complete reference.
+
+Composite Types
+---------------
+
+With PostgREST, you have two options to handle `composite type columns `_. On one hand you can use string representation and on the other you can handle it as you would a JSON column. Let's create a type and a table for this example:
+
+.. code-block:: postgres
+
+ create type dimension as (
+ length decimal(6,2),
+ width decimal (6,2),
+ height decimal (6,2),
+ unit text
+ );
+
+ create table products (
+ id int primary key,
+ size dimension
+ );
+
+ insert into products (id, size)
+ values (1, '(5.0,5.0,10.0,"cm")');
+
+Now, you could insert values using string representation as seen in the example above.
+
+.. tabs::
+
+ .. code-tab:: http
+
+ POST /products HTTP/1.1
+ Content-Type: application/json
+
+ { "id": 2, "size": "(0.7,0.5,1.8,\"m\")" }
+
+ .. code-tab:: bash Curl
+
+ curl "http://localhost:3000/products" \
+ -X POST -H "Content-Type: application/json" \
+ -d @- << EOF
+ { "id": 2, "size": "(0.7,0.5,1.8,\"m\")" }
+ EOF
+
+Or, you could insert the data in JSON format. The following request is equivalent to the previous one:
+
+.. tabs::
+
+ .. code-tab:: http
+
+ POST /products HTTP/1.1
+ Content-Type: application/json
+
+ {
+ "id": 2,
+ "size": {
+ "length": 0.7,
+ "width": 0.5,
+ "height": 1.8,
+ "unit": "m"
+ }
+ }
+
+ .. code-tab:: bash Curl
+
+ curl "http://localhost:3000/products" \
+ -X POST -H "Content-Type: application/json" \
+ -d @- << EOF
+ {
+ "id": 2,
+ "size": {
+ "length": 0.7,
+ "width": 0.5,
+ "height": 1.8,
+ "unit": "m"
+ }
+ }
+ EOF
+
+You can also query data using the arrow operators as you would for :ref:`JSON columns `.
+
+Ranges
+------
+
+To illustrate how to work with `ranges `_, let's use the following table as an example:
+
+.. code-block:: postgres
+
+ create table events (
+ id int primary key,
+ name text unique,
+ duration tsrange
+ );
+
+Now, to insert a new event, specify the ``duration`` value as a string representation of the ``tsrange`` type, for example:
+
+.. tabs::
+
+ .. code-tab:: http
+
+ POST /events HTTP/1.1
+ Content-Type: application/json
+
+ {
+ "id": 1,
+ "name": "New Year's Party",
+ "duration": "['2022-12-31 11:00','2023-01-01 06:00']"
+ }
+
+ .. code-tab:: bash Curl
+
+ curl "http://localhost:3000/events" \
+ -X POST -H "Content-Type: application/json" \
+ -d @- << EOF
+ {
+ "id": 1,
+ "name": "New Year's Party",
+ "duration": "['2022-12-31 11:00','2023-01-01 06:00']"
+ }
+ EOF
+
+You can use range :ref:`operators ` to filter the data. But what if you need get the events for the New Year 2023? Doing this filter ``events?duration=cs.2023-01-01`` will return an error because PostgreSQL needs an explicit cast to timestamp of the string value. A workaround would be to use a range starting and ending in the same date, like this:
+
+.. tabs::
+
+ .. code-tab:: http
+
+ GET /events?duration=cs.[2023-01-01,2023-01-01] HTTP/1.1
+
+ .. code-tab:: bash Curl
+
+ curl "http://localhost:3000/events?duration=cs.\[2023-01-01,2023-01-01\]"
+
+.. code-block:: json
+
+ [
+ {
+ "id": 1,
+ "name": "New Year's Party",
+ "duration": "[\"2022-12-31 11:00:00\",\"2023-01-01 06:00:00\"]"
+ }
+ ]
+
+.. _casting_range_to_json:
+
+Casting a Range to a JSON Object
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As you may have noticed, the ``tsrange`` value is returned as a string literal. To return it as a JSON value, first you need to create a function that will do the conversion from a ``tsrange`` type:
+
+.. code-block:: postgres
+
+ create or replace function tsrange_to_json(tsrange) returns json as $$
+ select json_build_object(
+ 'lower', lower($1)
+ , 'upper', upper($1)
+ , 'lower_inc', lower_inc($1)
+ , 'upper_inc', upper_inc($1)
+ );
+ $$ language sql;
+
+Then, create the cast using this function:
+
+.. code-block:: postgres
+
+ create cast (tsrange as json) with function tsrange_to_json(tsrange) as assignment;
+
+Finally, do the request :ref:`casting the range column `:
+
+.. tabs::
+
+ .. code-tab:: http
+
+ GET /events?select=id,name,duration::json HTTP/1.1
+
+ .. code-tab:: bash Curl
+
+ curl "http://localhost:3000/events?select=id,name,duration::json"
+
+.. code-block:: json
+
+ [
+ {
+ "id": 1,
+ "name": "New Year's Party",
+ "duration": {
+ "lower": "2022-12-31T11:00:00",
+ "upper": "2023-01-01T06:00:00",
+ "lower_inc": true,
+ "upper_inc": true
+ }
+ }
+ ]
+
+.. note::
+
+ If you don't want to modify casts for built-in types, an option would be to `create a custom type `_
+ for your own ``tsrange`` and add its own cast.
+
+ .. code-block:: postgres
+
+ create type mytsrange as range (subtype = timestamp, subtype_diff = tsrange_subdiff);
+
+ -- define column types and casting function analogously to the above example
+ -- ...
+
+ create cast (mytsrange as json) with function mytsrange_to_json(mytsrange) as assignment;
+
hstore
------
diff --git a/docs/index.rst b/docs/index.rst
index ea08aa3ad..9448cd089 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -204,10 +204,7 @@ These are recipes that'll help you address specific use-cases.
how-tos/*
-- :doc:`how-tos/casting-type-to-custom-json`
-- :doc:`how-tos/embedding-table-from-another-schema`
- :doc:`how-tos/providing-images-for-img`
-- `How PostgreSQL triggers work when called with a PostgREST PATCH HTTP request `_
- :doc:`how-tos/working-with-postgresql-data-types`
Ecosystem