3 points par GN⁺ 2023-12-19 | 1 commentaires | Partager sur WhatsApp
  • 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_anon les droits sur api.todos et api.todos_id_seq
  • L’assemblage du HTML est pris en charge par des fonctions SQL, où api.sanitize_html, api.html_todo et api.html_all_todos gè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-area ou 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 plmustache pour 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_anon
    • grant all sur la table api.todos
    • grant usage, select sur la séquence api.todos_id_seq
  • Pour que PostgREST reconnaisse les requêtes du navigateur Accept: text/html comme du HTML, il faut ajouter un gestionnaire de type de média
    • création du domaine "text/html" à partir de text
    • cette configuration permet à PostgREST de renvoyer des documents HTML bruts

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 List ainsi 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 HTML
    • api.html_todo(api.todos) : met en forme un élément de to-do unique en fragment HTML
    • api.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 message There is nothing else to do.
  • api.html_todo et api.html_all_todos sont des fonctions internes servant à créer les templates de liste, et ne sont pas utilisées directement comme endpoints PostgREST
  • api.add_todo(_task text) insère un nouveau to-do dans api.todos, puis renvoie le HTML complet de la liste mise à jour
  • La version mise à jour de api.index() inclut htmx et le formulaire de saisie
    • hx-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 _task vers /rpc/add_todo
    • hx-target="#todo-list-area" : insère le HTML de réponse dans la zone de liste des to-do
    • hx-trigger="submit" : envoie la requête lors de la soumission du formulaire
    • hx-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_todo est é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 htmx
    • hx-post="/rpc/change_todo_state" : envoie une requête AJAX POST vers cet endpoint
    • hx-vals='{"_id": ..., "_done": ...}' : ajoute les paramètres de requête au lieu d’utiliser des champs cachés
    • hx-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 éditable
    • hx-target="#todo-edit-area-..." : remplace uniquement la zone de la tâche concernée, et non la liste entière
    • hx-vals ajoute des paramètres à la requête GET et, lorsqu’il s’agit d’une colonne de table, l’opérateur eq. 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"}'
  • 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 colonne done
    • api.change_todo_task(_id int, _task text) : met à jour la colonne task
    • api.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

 
GN⁺ 2023-12-19
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

    • Comme c’est une partie centrale de la stack Supabase, nous employons Steve, le mainteneur principal, pour qu’il travaille principalement sur PostgREST, comme mentionné.
      https://github.com/steve-chavez
    • Ils sont désormais 13. Mais cela ne fait toujours que 1 529 $ par mois.
    • Je ne savais pas que PostgREST faisait partie de Supabase. Je pensais que Supabase était un produit copié, créé de zéro.
    • J’entends déjà les phrases toutes faites qui reviennent toujours dans ce genre de situation : « les utilisateurs n’ont aucune obligation de te soutenir financièrement », « si ça ne te plaît pas, il ne fallait pas publier sous licence permissive », etc.
      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.

    • Clairement, c’est plutôt destiné à des sites web ou à des applications légères. Mais dans ce périmètre, on peut quand même en tirer pas mal de choses.
    • J’expérimente en ce moment pour voir si Sqitch peut rendre la maintenance plus facile. Ça ressemble toujours à un hack, et j’ai encore des doutes sur son utilisabilité réelle, mais c’est amusant et j’apprends plusieurs fonctionnalités avancées de Postgres.
      https://sqitch.org/
    • Honnêtement, je me demande quelle partie de notre application web principale ne deviendrait pas 100 fois plus simple si on utilisait ça.
    • Pour l’instant, je pense que oui. Il ne manque plus qu’un moyen de compiler et déployer les routes HTMX sur le serveur de base de données dans le cadre du processus de CI.
  • 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.

    • On dirait qu’une nouvelle génération réapprend des choses qui avaient été abandonnées à l’époque pour de bonnes raisons.
      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.
    • Difficile d’appeler ça moderne. Oracle avait déjà ce genre de choses il y a plus de 25 ans.
  • 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.

    • On dirait que cette équipe a choisi un outil inadapté à la tâche. Si la plupart de vos endpoints ont une logique backend complexe, il ne faut pas utiliser PostgREST.
      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.
    • J’utilise beaucoup PostGREST, mais avec de solides garde-fous. À mon avis, il faut toujours avoir une vraie couche d’API, et considérer cela seulement comme un moyen auxiliaire de faciliter le chargement de base.
      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.
    • Les procédures stockées ne sont-elles pas, au fond, du SQL sous forme de fonctions ?
      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.

    • Au milieu des années 90, j’ai visité Compuserve dans l’Ohio. Le Web venait tout juste de démarrer, et un ingénieur rencontré là-bas m’a montré une boutique de musique qu’il construisait en renvoyant du HTML depuis des procédures stockées SQL :)
    • Si ça vous paraît cool, Omnigres vaut aussi le détour.
      https://github.com/omnigres/omnigres
    • Nous sommes déjà passés par là. Au début c’est propre, mais ce n’est pas bon pour la maintenance à long terme, le support ni la formation.
      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.
    • Le backend et le frontend existent toujours. Ils sont simplement tous les deux directement entremêlés dans le code de la base de données.
    • En fin de compte, on est revenus au mainframe. Cette fois, le navigateur n’est que le terminal.
  • 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.

    • Ça ne m’intéresse pas du tout. J’ai déjà vécu ça avec PHP. Ce pattern ne dispose pas de fonctionnalités importantes comme l’analyse statique, les tests locaux, le refactoring ou les changements massifs livrés atomiquement.
      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 ?

    • Pour servir des sites statiques, j’utilise Astro(https://astro.build), et PostgREST Htmx pour des composants simples centrés sur les données
    • À moyenne ou grande échelle, je pense qu’il faut une couche d’abstraction séparée, c’est-à-dire une API
      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

    • Au mauvais vieux temps, les vues parlaient directement à la base de données, si bien que les XSS étaient omniprésentes. Ensuite, en plaçant des moteurs de templates et des modèles au milieu, le problème a semblé résolu
      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
    • Exact. htmx exécutera volontiers ce script. La démo avait le même bug : https://github.com/bigskysoftware/htmx/pull/1995/files
    • Ça ressemble à une attaque de l’homme de paille contre une documentation d’exemple. HTMX ne fait que servir du HTML, et les entrées doivent être assainies d’une manière ou d’une autre
      Le HTML peut être rendu avec n’importe quel langage de templates qui effectue ce traitement