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:
Steve Chavez
2023-10-28 12:00:37 -05:00
committed by GitHub
co-authored by Laurence Isla
parent 5c314e3f02
commit b85ee953b7
3 changed files with 138 additions and 143 deletions
@@ -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
+2
View File
@@ -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