PostgREST : fournir du contenu HTML avec htmx
(postgrest.org)- Si une fonction PostgreSQL renvoie directement du
text/html, il est possible de créer une application de to-do qui reçoit le résultat de requêtes AJAX et remplace une partie du DOM avec seulement PostgREST et htmx - L’exemple ajoute un type de média personnalisé pour traiter les requêtes
Accept: text/html, sans authentification, en accordant àweb_anonles droits surapi.todosetapi.todos_id_seq - L’assemblage du HTML est pris en charge par des fonctions SQL, où
api.sanitize_html,api.html_todoetapi.html_all_todosgèrent l’échappement des entrées et le rendu de la liste - Le fonctionnement de l’interface repose sur
hx-post,hx-get,hx-target,hx-trigger,hx-vals,hx-headers, et le HTML de réponse remplace#todo-list-areaou chaque zone d’édition individuelle - Cette configuration relève davantage d’une preuve de concept, et côté PostgREST, des travaux sont en cours sur
plmustachepour améliorer le traitement du HTML
Rôle de PostgREST et de htmx
- PostgREST renvoie du contenu HTML, et htmx utilise le HTML reçu en réponse à des requêtes AJAX pour remplacer des éléments dans le DOM
- Comme htmx attend des réponses HTML, il s’accorde bien avec un flux où le serveur renvoie des fragments HTML au lieu de JSON
- L’exemple montre une configuration possible lorsqu’on utilise ces deux technologies ensemble, sous forme de preuve de concept
- PostgREST travaille sur plmustache pour encore améliorer la gestion du HTML
Préparation de l’application de to-do
- L’exemple est une application de to-do basée sur Tutorial 0 - Get it Running
- Pour simplifier, aucune authentification n’est utilisée, et les autorisations nécessaires sont accordées à l’utilisateur
web_anongrant allsur la tableapi.todosgrant usage, selectsur la séquenceapi.todos_id_seq
- Pour que PostgREST reconnaisse les requêtes du navigateur
Accept: text/htmlcomme du HTML, il faut ajouter un gestionnaire de type de média- création du domaine
"text/html"à partir detext - cette configuration permet à PostgREST de renvoyer des documents HTML bruts
- création du domaine
Retourner la page HTML de base
- La fonction
api.index()renvoie"text/html"et génère le document HTML de base - La page inclut le titre
PostgREST + HTMX To-Do Listainsi que les ressources suivantes - Dans le navigateur, cette page peut être ouverte via
http://localhost:3000/rpc/index
Rendu de la liste et ajout de to-do
- Trois fonctions SQL sont utilisées pour produire la liste des to-do en HTML
api.sanitize_html(text): remplace les caractères&,",>,<,'par des entités HTMLapi.html_todo(api.todos): met en forme un élément de to-do unique en fragment HTMLapi.html_all_todos(): concatène tous les éléments en une seule chaîne HTML et, s’il n’y a aucun élément, renvoie le messageThere is nothing else to do.
api.html_todoetapi.html_all_todossont des fonctions internes servant à créer les templates de liste, et ne sont pas utilisées directement comme endpoints PostgRESTapi.add_todo(_task text)insère un nouveau to-do dansapi.todos, puis renvoie le HTML complet de la liste mise à jour- La version mise à jour de
api.index()inclut htmx et le formulaire de saisiehx-headers='{"Accept": "text/html"}'est placé sur l’élément parent afin que les requêtes htmx enfants demandent des réponses HTML- sans cet en-tête, PostgREST ne reconnaît pas la requête comme une demande de HTML
- Le formulaire d’ajout de to-do fonctionne via les attributs htmx
hx-post="/rpc/add_todo": envoie par POST la valeur de_taskvers/rpc/add_todohx-target="#todo-list-area": insère le HTML de réponse dans la zone de liste des to-dohx-trigger="submit": envoie la requête lors de la soumission du formulairehx-on="htmx:afterRequest: this.reset()": vide le formulaire une fois la requête terminée
- Il faut rafraîchir le cache de schéma pour prendre en compte les nouvelles fonctions et modifications
- On peut ensuite vérifier l’affichage de la liste et l’ajout de nouveaux to-do sur
http://localhost:3000/rpc/index
Édition, suppression et changement d’état
api.html_todoest étendue pour inclure, pour chaque élément, une interface de changement d’état, d’édition et de suppression- Le changement d’état est géré par un
<form>et des attributs htmxhx-post="/rpc/change_todo_state": envoie une requête AJAX POST vers cet endpointhx-vals='{"_id": ..., "_done": ...}': ajoute les paramètres de requête au lieu d’utiliser des champs cachéshx-trigger="click": déclenche la requête au clic sur l’élément
- Le bouton d’édition remplace la tâche individuelle par un champ de saisie
hx-get="/rpc/html_editable_task": appelle l’endpoint qui renvoie un champ HTML éditablehx-target="#todo-edit-area-...": remplace uniquement la zone de la tâche concernée, et non la liste entièrehx-valsajoute des paramètres à la requête GET et, lorsqu’il s’agit d’une colonne de table, l’opérateureq.est nécessaire
- Le bouton de suppression envoie une demande de suppression du to-do concerné via
hx-post="/rpc/delete_todo" api.html_editable_task(_id int)renvoie le HTML d’un champ de saisie permettant d’éditer un to-do spécifique- soumet le nom de tâche modifié à
hx-post="/rpc/change_todo_task" hx-trigger="submit,focusout"déclenche la mise à jour à la soumission ou à la perte de focus- inclut
hx-headers='{"Accept": "text/html"}'
- soumet le nom de tâche modifié à
- Les endpoints de modification de données renvoient tous le HTML complet de la liste après la modification
api.change_todo_state(_id int, _done boolean): met à jour la colonnedoneapi.change_todo_task(_id int, _task text): met à jour la colonnetaskapi.delete_todo(_id int): supprime le to-do correspondant à_id
- Après avoir rafraîchi le cache de schéma, il devient possible d’éditer, supprimer et marquer comme terminé des to-do sur
http://localhost:3000/rpc/index
1 commentaires
Avis sur Hacker News
PostgREST est l’un de mes projets open source préférés. Je pense que si Supabase est devenue une entreprise valorisée à 1 milliard de dollars, c’est en grande partie grâce à l’excellente conception de PostgREST et de Postgres.
Je ne sais pas comment Supabase finance ce projet, mais j’espère que c’est à très grande échelle. C’est triste de voir la réalité du financement de l’open source : un projet utilisé comme dépendance critique par au moins des centaines d’entreprises générant du chiffre d’affaires n’a pourtant que 12 contributeurs payants.
https://www.patreon.com/postgrest/about
https://github.com/steve-chavez
C’est pour ça que désormais je n’utilise que l’AGPLv3 ou des licences similaires. Si vous voulez l’utiliser commercialement, vous payez 1 % de votre chiffre d’affaires brut, par exemple.
Comme preuve de concept, c’est cool, et l’implémentation mérite des compliments, mais à maintenir dans une webapp un tant soit peu non triviale, ça ressemble à un cauchemar.
https://sqitch.org/
Je me demande si ce genre de fonctionnalité ou de pattern de codage est nouveau, ou s’il est utilisé aussi dans des applications modernes.
CouchDB, une base de données documentaire JSON, fournissait une API basée sur HTTP et intégrait des méthodes de liste/détail capables de répondre dans n’importe quel format constructible dans un interpréteur JavaScript. Autrement dit, le client pouvait appeler directement la base de données et recevoir du HTML ou du JSON, sans serveur.
Mais après la v1, ils ont arrêté de travailler dans cette direction, parce que la maintenance devenait un cauchemar. Beaucoup se souviennent sans doute du bon vieux temps des anciens fichiers PHP ou ASP où les requêtes SQL et le HTML étaient mélangés dans un même fichier ; ici, ça ne semble pas très différent.
Il y a environ 13 ans, j’adorais vraiment les fonctionnalités web de CouchDB pour un projet personnel, mais c’était assez pénible à gérer en équipe.
La seule application PostgREST sur laquelle j’ai travaillé était horrible. Parce que, comme avec la plupart de ces frameworks « simples », ils ne sont simples que jusqu’à ce que les exigences deviennent complexes.
Les auteurs initiaux ont fini par utiliser un tas de procédures stockées dans la base de données pour obtenir le résultat voulu, ce qui a entraîné des problèmes de scalabilité. Comme toujours, la solution consiste à revenir au SQL.
PostgREST a été conçu pour des applications CRUD, et beaucoup des applications que je rencontre entrent dans cette catégorie. La logique backend personnalisée dont on a besoin ponctuellement peut être traitée via un serveur séparé ou des fonctions edge.
Comme avec tous ces outils, dès qu’on arrive aux exigences du monde réel, le coût d’adaptation à l’outil devient pire que ce qu’il était censé remplacer : SQL + un langage et un framework quelconques. PostGREST devient déjà complexe, et ce genre de fonctionnalités supplémentaires le rend moins attrayant à mes yeux.
Avant que Rails n’arrive et ne convainque tout le monde que mettre la logique métier dans un langage serveur lent était une bonne idée, tout le monde construisait des applis comme ça.
C’est une stack de développement web vraiment épurée. Il n’y a que HTML et la base de données, pas besoin de backend ni de frontend.
https://github.com/omnigres/omnigres
Quand l’éclat de HTMX se sera estompé, la personne qui en héritera finira par admettre qu’il faut tout réécrire depuis zéro.
J’ai utilisé PostgREST sans HTMX sur un ancien projet, et j’ai été impressionné de voir jusqu’où on pouvait le pousser.
HTMX semble aussi bien s’y prêter, mais je ne sais pas trop dans quelle mesure j’aurais envie de maintenir des templates HTMX à l’intérieur de fonctions SQL.
C’est bien pour bricoler, mais pas adapté pour construire quelque chose de stable.
Du point de vue de Haskell, PostgREST est intéressant. Parce qu’il ressemble à un projet tellement évident.
Mais c’est précisément pour ça qu’il est génial. C’est une idée vraiment haskellienne, et j’aime ça.
De quels outils supplémentaires pensez-vous avoir besoin pour faire de ce concept une stack maintenable offrant une bonne expérience utilisateur dans des applications moyennes à grandes ?
J’ai moi aussi essayé de construire quelque chose de similaire au-dessus de SQL ; la stack technique était une couche OpenAPI implémentée avec du SQL à templates Jinja et du YML, mais je pense tout de même que je la limiterais à des outils internes. C’est ici : https://jinj.at
Show HN associé : rendre du HTML depuis SQL avec pg_render
https://news.ycombinator.com/item?id=38677852
Je veux noter une tâche dans cette appli pour ne pas l’oublier. Ma tâche est la suivante :
Dans ce cas, la colonne task ne semble être assainie nulle part
Mais maintenant que la base de données est la vue, est-ce que les XSS reviennent ? C’est un problème qu’on peut prévenir
Le HTML peut être rendu avec n’importe quel langage de templates qui effectue ce traitement