doc: media type handlers (#689)
* reference: media type handlers * Use function instead of computed field in editable task --------- Co-authored-by: Laurence Isla <lau.isla.c@gmail.com>
This commit is contained in:
co-authored by
Laurence Isla
parent
5c314e3f02
commit
b85ee953b7
@@ -23,13 +23,12 @@ To simplify things, we won't be using authentication, so grant all permissions o
|
|||||||
grant all on api.todos to web_anon;
|
grant all on api.todos to web_anon;
|
||||||
grant usage, select on sequence api.todos_id_seq to web_anon;
|
grant usage, select on sequence api.todos_id_seq to web_anon;
|
||||||
|
|
||||||
Next, add the ``text/html`` media type to the :ref:`raw-media-types` configuration.
|
Next, add the ``text/html`` media type as a DOMAIN. With this, PostgREST can identify the request made by your web browser (with the ``Accept: text/html`` header)
|
||||||
With this, PostgREST can identify the request made by your web browser (with the ``Accept: text/html`` header) and return a raw HTML document file.
|
and return a raw HTML document file.
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: postgres
|
||||||
|
|
||||||
# tutorial.conf
|
create domain "text/html" as text;
|
||||||
raw-media-types = "text/html"
|
|
||||||
|
|
||||||
Creating an HTML Response
|
Creating an HTML Response
|
||||||
-------------------------
|
-------------------------
|
||||||
@@ -38,12 +37,8 @@ Let's create a function that returns a basic HTML file, using `Tailwind CSS <htt
|
|||||||
|
|
||||||
.. code-block:: postgres
|
.. code-block:: postgres
|
||||||
|
|
||||||
create or replace function api.index() returns text
|
create or replace function api.index() returns "text/html" as $$
|
||||||
language plpgsql
|
select $html$
|
||||||
as $$
|
|
||||||
begin
|
|
||||||
perform set_config('response.headers','[{"Content-Type": "text/html; charset=utf-8"}]', true);
|
|
||||||
return $html$
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
@@ -62,7 +57,7 @@ Let's create a function that returns a basic HTML file, using `Tailwind CSS <htt
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
$html$;
|
$html$;
|
||||||
end $$;
|
$$ language sql;
|
||||||
|
|
||||||
The web browser will open the web page at ``http://localhost:3000/rpc/index``.
|
The web browser will open the web page at ``http://localhost:3000/rpc/index``.
|
||||||
|
|
||||||
@@ -77,9 +72,7 @@ Now, let's show a list of the to-dos already inserted in the database.
|
|||||||
|
|
||||||
.. code-block:: postgres
|
.. code-block:: postgres
|
||||||
|
|
||||||
create or replace function api.html_todo(api.todos) returns text
|
create or replace function api.html_todo(api.todos) returns text as $$
|
||||||
language sql stable
|
|
||||||
as $$
|
|
||||||
select format($html$
|
select format($html$
|
||||||
<li class="py-3">
|
<li class="py-3">
|
||||||
<span class="ml-2 %2$s">
|
<span class="ml-2 %2$s">
|
||||||
@@ -91,11 +84,9 @@ Now, let's show a list of the to-dos already inserted in the database.
|
|||||||
case when $1.done then 'line-through text-gray-400' else '' end,
|
case when $1.done then 'line-through text-gray-400' else '' end,
|
||||||
$1.task
|
$1.task
|
||||||
);
|
);
|
||||||
$$;
|
$$ language sql stable;
|
||||||
|
|
||||||
create or replace function api.html_all_todos() returns text
|
create or replace function api.html_all_todos() returns text as $$
|
||||||
language sql
|
|
||||||
as $$
|
|
||||||
select coalesce(
|
select coalesce(
|
||||||
'<ul id="todo-list" role="list" class="divide-y divide-gray-700 text-gray-100">'
|
'<ul id="todo-list" role="list" class="divide-y divide-gray-700 text-gray-100">'
|
||||||
|| string_agg(api.html_todo(t), '' order by t.id) ||
|
|| string_agg(api.html_todo(t), '' order by t.id) ||
|
||||||
@@ -103,7 +94,7 @@ Now, let's show a list of the to-dos already inserted in the database.
|
|||||||
'<p class="text-gray-100">There is nothing else to do.</p>'
|
'<p class="text-gray-100">There is nothing else to do.</p>'
|
||||||
)
|
)
|
||||||
from api.todos t;
|
from api.todos t;
|
||||||
$$;
|
$$ language sql;
|
||||||
|
|
||||||
These two functions are used to build the to-do list template. We won't use them as PostgREST endpoints.
|
These two functions are used to build the to-do list template. We won't use them as PostgREST endpoints.
|
||||||
|
|
||||||
@@ -119,22 +110,13 @@ Next, let's add an endpoint to register a to-do in the database and modify the `
|
|||||||
|
|
||||||
.. code-block:: postgres
|
.. code-block:: postgres
|
||||||
|
|
||||||
create or replace function api.add_todo(_task text) returns text
|
create or replace function api.add_todo(_task text) returns "text/html" as $$
|
||||||
language plpgsql
|
|
||||||
as $$
|
|
||||||
begin
|
|
||||||
perform set_config('response.headers','[{"Content-Type": "text/html; charset=utf-8"}]', true);
|
|
||||||
insert into api.todos(task) values (_task);
|
insert into api.todos(task) values (_task);
|
||||||
return api.html_all_todos();
|
select api.html_all_todos();
|
||||||
end;
|
$$ language sql;
|
||||||
$$;
|
|
||||||
|
|
||||||
create or replace function api.index() returns text
|
create or replace function api.index() returns "text/html" as $$
|
||||||
language plpgsql
|
select $html$
|
||||||
as $$
|
|
||||||
begin
|
|
||||||
perform set_config('response.headers','[{"Content-Type": "text/html; charset=utf-8"}]', true);
|
|
||||||
return $html$
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
@@ -168,7 +150,7 @@ Next, let's add an endpoint to register a to-do in the database and modify the `
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
$html$;
|
$html$;
|
||||||
end $$;
|
$$ language sql;
|
||||||
|
|
||||||
- The ``/rpc/add_todo`` endpoint allows us to add a new to-do using the ``_task`` parameter and returns an ``html`` with all the to-dos in the database.
|
- The ``/rpc/add_todo`` endpoint allows us to add a new to-do using the ``_task`` parameter and returns an ``html`` with all the to-dos in the database.
|
||||||
|
|
||||||
@@ -197,9 +179,7 @@ Now, let's modify ``api.html_todo`` and make it more functional.
|
|||||||
|
|
||||||
.. code-block:: postgres
|
.. code-block:: postgres
|
||||||
|
|
||||||
create or replace function api.html_todo(api.todos) returns text
|
create or replace function api.html_todo(api.todos) returns text as $$
|
||||||
language sql stable
|
|
||||||
as $$
|
|
||||||
select format($html$
|
select format($html$
|
||||||
<li class="py-3">
|
<li class="py-3">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
@@ -216,8 +196,8 @@ Now, let's modify ``api.html_todo`` and make it more functional.
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="p-1.5 rounded-full hover:bg-gray-700 focus:ring-gray-800"
|
<button class="p-1.5 rounded-full hover:bg-gray-700 focus:ring-gray-800"
|
||||||
hx-get="/todos?select=html_editable_task"
|
hx-get="/rpc/html_editable_task"
|
||||||
hx-vals='{"id": "eq.%1$s"}'
|
hx-vals='{"_id": "%1$s"}'
|
||||||
hx-target="#todo-edit-area-%1$s"
|
hx-target="#todo-edit-area-%1$s"
|
||||||
hx-trigger="click">
|
hx-trigger="click">
|
||||||
<svg class="w-4 h-4 text-blue-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
|
<svg class="w-4 h-4 text-blue-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
|
||||||
@@ -243,7 +223,7 @@ Now, let's modify ``api.html_todo`` and make it more functional.
|
|||||||
$1.task,
|
$1.task,
|
||||||
(not $1.done)::text
|
(not $1.done)::text
|
||||||
);
|
);
|
||||||
$$;
|
$$ language sql stable;
|
||||||
|
|
||||||
Let's deconstruct the new htmx features added:
|
Let's deconstruct the new htmx features added:
|
||||||
|
|
||||||
@@ -258,7 +238,7 @@ Let's deconstruct the new htmx features added:
|
|||||||
|
|
||||||
- For the first ``<button>``:
|
- For the first ``<button>``:
|
||||||
|
|
||||||
+ ``hx-get="/todos?select=html_editable_task"``: it does an AJAX GET request to that endpoint.
|
+ ``hx-get="/rpc/html_editable_task"``: it does an AJAX GET request to that endpoint.
|
||||||
It returns an HTML with an input that will allow us to edit the task.
|
It returns an HTML with an input that will allow us to edit the task.
|
||||||
|
|
||||||
+ ``hx-target="#todo-edit-area"``: the returned HTML will replace the element with this id.
|
+ ``hx-target="#todo-edit-area"``: the returned HTML will replace the element with this id.
|
||||||
@@ -272,13 +252,11 @@ Let's deconstruct the new htmx features added:
|
|||||||
+ ``hx-post="/rpc/delete_todo"``: this post request will delete the corresponding to-do.
|
+ ``hx-post="/rpc/delete_todo"``: this post request will delete the corresponding to-do.
|
||||||
|
|
||||||
Clicking on the first button will enable the task editing.
|
Clicking on the first button will enable the task editing.
|
||||||
That's why we create the ``api.html_editable_task`` :ref:`computed field <computed_cols>` and use the ``api.todos`` table as an endpoint:
|
That's why we create the ``api.html_editable_task`` function as an endpoint:
|
||||||
|
|
||||||
.. code-block:: postgres
|
.. code-block:: postgres
|
||||||
|
|
||||||
create or replace function api.html_editable_task(api.todos)
|
create or replace function api.html_editable_task(_id int) returns "text/html" as $$
|
||||||
returns text
|
|
||||||
language sql as $$
|
|
||||||
select format ($html$
|
select format ($html$
|
||||||
<form id="edit-task-%1$s"
|
<form id="edit-task-%1$s"
|
||||||
hx-post="/rpc/change_todo_task"
|
hx-post="/rpc/change_todo_task"
|
||||||
@@ -290,47 +268,33 @@ That's why we create the ``api.html_editable_task`` :ref:`computed field <comput
|
|||||||
id="task-%1$s" type="text" name="_task" value="%2$s" autofocus>
|
id="task-%1$s" type="text" name="_task" value="%2$s" autofocus>
|
||||||
</form>
|
</form>
|
||||||
$html$,
|
$html$,
|
||||||
$1.id,
|
id,
|
||||||
$1.task
|
task
|
||||||
);
|
)
|
||||||
$$;
|
from api.todos
|
||||||
|
where id = _id;
|
||||||
|
$$ language sql;
|
||||||
|
|
||||||
We could use a function too, but this demonstrates that we can build HTML components (specially for a list) directly from a table by using computed fields.
|
|
||||||
In this example, this will return an input field that allows us to edit the corresponding to-do task.
|
In this example, this will return an input field that allows us to edit the corresponding to-do task.
|
||||||
|
|
||||||
Finally, let's add the endpoints that will modify and delete the to-dos in the database.
|
Finally, let's add the endpoints that will modify and delete the to-dos in the database.
|
||||||
|
|
||||||
.. code-block:: postgres
|
.. code-block:: postgres
|
||||||
|
|
||||||
create or replace function api.change_todo_state(_id int, _done boolean) returns text
|
create or replace function api.change_todo_state(_id int, _done boolean) returns "text/html" as $$
|
||||||
language plpgsql
|
|
||||||
as $$
|
|
||||||
begin
|
|
||||||
perform set_config('response.headers','[{"Content-Type": "text/html; charset=utf-8"}]', true);
|
|
||||||
update api.todos set done = _done where id = _id;
|
update api.todos set done = _done where id = _id;
|
||||||
return api.html_all_todos();
|
select api.html_all_todos();
|
||||||
end;
|
$$ language sql;
|
||||||
$$;
|
|
||||||
|
|
||||||
create or replace function api.change_todo_task(_id int, _task text) returns text
|
create or replace function api.change_todo_task(_id int, _task text) returns "text/html" as $$
|
||||||
language plpgsql
|
|
||||||
as $$
|
|
||||||
begin
|
|
||||||
perform set_config('response.headers','[{"Content-Type": "text/html; charset=utf-8"}]', true);
|
|
||||||
update api.todos set task = _task where id = _id;
|
update api.todos set task = _task where id = _id;
|
||||||
return api.html_all_todos();
|
select api.html_all_todos();
|
||||||
end;
|
$$ language sql;
|
||||||
$$;
|
|
||||||
|
|
||||||
create or replace function api.delete_todo(_id int) returns text
|
create or replace function api.delete_todo(_id int) returns "text/html" as $$
|
||||||
language plpgsql
|
|
||||||
as $$
|
|
||||||
begin
|
|
||||||
perform set_config('response.headers','[{"Content-Type": "text/html; charset=utf-8"}]', true);
|
|
||||||
delete from api.todos where id = _id;
|
delete from api.todos where id = _id;
|
||||||
return api.html_all_todos();
|
select api.html_all_todos();
|
||||||
end;
|
$$ language sql;
|
||||||
$$;
|
|
||||||
|
|
||||||
All of those functions return an HTML list of to-dos that will replace the outdated one:
|
All of those functions return an HTML list of to-dos that will replace the outdated one:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
Media Type Handlers
|
||||||
|
###################
|
||||||
|
|
||||||
|
PostgREST offers builtin handlers for common media types such as ``application/json`` and ``text/csv``. You can add handlers for other media types or override the builtin handlers.
|
||||||
|
|
||||||
|
Standard Media Types Handlers
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Builtins handlers are provided for the following standard media types:
|
||||||
|
|
||||||
|
* ``application/json``
|
||||||
|
* ``text/csv``
|
||||||
|
* ``application/geo+json``
|
||||||
|
* ``application/x-www-form-urlencoded``
|
||||||
|
* ``*/*``, uses the same handler as ``application/json``.
|
||||||
|
|
||||||
|
Vendor Media Types Handlers
|
||||||
|
===========================
|
||||||
|
|
||||||
|
PostgREST also includes its own vendored media types, handlers for these are provided but cannot be overridden.
|
||||||
|
|
||||||
|
* ``application/vnd.pgrst.object``
|
||||||
|
* ``application/vnd.pgrst.array``
|
||||||
|
* ``application/vnd.pgrst.plan``
|
||||||
|
|
||||||
|
Custom Media Type Handlers
|
||||||
|
==========================
|
||||||
|
|
||||||
|
TODO
|
||||||
@@ -11,6 +11,7 @@ backoff
|
|||||||
balancer
|
balancer
|
||||||
booleans
|
booleans
|
||||||
Bouscal
|
Bouscal
|
||||||
|
Builtins
|
||||||
buildpack
|
buildpack
|
||||||
Bytea
|
Bytea
|
||||||
Cardano
|
Cardano
|
||||||
@@ -195,6 +196,7 @@ url
|
|||||||
urlencoded
|
urlencoded
|
||||||
urls
|
urls
|
||||||
variadic
|
variadic
|
||||||
|
vendored
|
||||||
verifier
|
verifier
|
||||||
versioning
|
versioning
|
||||||
Vondra
|
Vondra
|
||||||
|
|||||||
Reference in New Issue
Block a user