PostgREST: Cung cấp nội dung HTML bằng htmx
(postgrest.org)- Nếu một hàm PostgreSQL trả trực tiếp
text/html, bạn có thể tạo ứng dụng to-do chỉ với PostgREST và htmx, nhận kết quả từ yêu cầu AJAX rồi thay thế một phần DOM - Ví dụ cấp quyền cho
web_anonđối vớiapi.todosvàapi.todos_id_seqmà không cần xác thực, đồng thời thêm kiểu media tùy chỉnh để xử lý các yêu cầuAccept: text/html - Việc ghép HTML do các hàm SQL đảm nhiệm;
api.sanitize_html,api.html_todo,api.html_all_todosxử lý escape giá trị đầu vào và render danh sách - Hành vi trên giao diện được cấu hình bằng
hx-post,hx-get,hx-target,hx-trigger,hx-vals,hx-headers; HTML phản hồi sẽ thay thế#todo-list-areahoặc từng vùng chỉnh sửa riêng lẻ - Cấu hình này gần với một bằng chứng khái niệm, và phía PostgREST đang phát triển
plmustacheđể cải thiện việc xử lý HTML
Vai trò của PostgREST và htmx
- PostgREST trả về nội dung HTML, còn htmx dùng HTML nhận được từ kết quả yêu cầu AJAX để thay thế các phần tử bên trong DOM
- Vì htmx kỳ vọng phản hồi HTML, nó rất phù hợp với luồng trong đó server trả về các mảnh HTML thay vì JSON
- Ví dụ này là một bằng chứng khái niệm cho thấy cấu hình có thể thực hiện khi dùng hai công nghệ này cùng nhau
- PostgREST đang phát triển plmustache để cải thiện hơn nữa khía cạnh xử lý HTML
Thiết lập chuẩn bị cho ứng dụng to-do
- Ví dụ là một ứng dụng to-do dựa trên Tutorial 0 - Get it Running
- Để đơn giản hóa, không sử dụng xác thực và cấp các quyền cần thiết cho người dùng
web_anongrant alltrên bảngapi.todosgrant usage, selecttrên sequenceapi.todos_id_seq
- Để PostgREST nhận diện yêu cầu
Accept: text/htmltừ trình duyệt là HTML, cần thêm media type handler- Tạo domain
"text/html"dưới dạngtext - Thiết lập này cho phép PostgREST trả về tài liệu HTML thô
- Tạo domain
Trả về trang HTML cơ bản
- Hàm
api.index()trả về"text/html"và tạo tài liệu HTML cơ bản - Trang bao gồm tiêu đề
PostgREST + HTMX To-Do Listcùng các tài nguyên sau - Trên trình duyệt, có thể mở trang này tại
http://localhost:3000/rpc/index
Render danh sách và thêm to-do
- Ba hàm SQL được dùng để tạo danh sách to-do hiện có dưới dạng HTML
api.sanitize_html(text): thay thế các ký tự&,",>,<,'bằng HTML entityapi.html_todo(api.todos): định dạng một mục to-do đơn lẻ thành mảnh HTMLapi.html_all_todos(): ghép toàn bộ các mục thành một chuỗi HTML; nếu không có mục nào thì trả về thông báoThere is nothing else to do.
api.html_todovàapi.html_all_todoslà các hàm dùng nội bộ để tạo template danh sách, không được dùng trực tiếp làm endpoint PostgRESTapi.add_todo(_task text)chèn một to-do mới vàoapi.todos, rồi trả về HTML danh sách đầy đủ đã cập nhậtapi.index()đã cập nhật bao gồm htmx và form nhập liệu- Đặt
hx-headers='{"Accept": "text/html"}'ở phần tử cha để các yêu cầu htmx con yêu cầu phản hồi HTML - Nếu không có header này, PostgREST sẽ không nhận diện yêu cầu là HTML
- Đặt
- Form thêm to-do hoạt động bằng các thuộc tính htmx
hx-post="/rpc/add_todo": gửi yêu cầu POST giá trị nhập_tasktới/rpc/add_todohx-target="#todo-list-area": đưa HTML phản hồi vào vùng danh sách to-dohx-trigger="submit": gửi yêu cầu khi submit formhx-on="htmx:afterRequest: this.reset()": xóa form sau khi yêu cầu hoàn tất
- Cần làm mới schema cache để phản ánh hàm mới và các thay đổi
- Sau đó, có thể kiểm tra việc hiển thị danh sách và thêm to-do mới tại
http://localhost:3000/rpc/index
Chỉnh sửa, xóa và thay đổi trạng thái hoàn thành
api.html_todođược mở rộng để mỗi mục bao gồm UI đổi trạng thái hoàn thành, chỉnh sửa và xóa- Việc đổi trạng thái hoàn thành được xử lý bằng
<form>và các thuộc tính htmxhx-post="/rpc/change_todo_state": gửi yêu cầu AJAX POST tới endpoint tương ứnghx-vals='{"_id": ..., "_done": ...}': thêm tham số yêu cầu thay vì dùng input ẩnhx-trigger="click": thực thi yêu cầu khi nhấp vào mục
- Nút chỉnh sửa biến từng tác vụ thành trường nhập liệu
hx-get="/rpc/html_editable_task": gọi endpoint trả về input HTML có thể chỉnh sửahx-target="#todo-edit-area-...": chỉ thay thế vùng tác vụ riêng lẻ thay vì toàn bộ danh sáchhx-valsthêm tham số cho yêu cầu GET; khi biểu thị cột bảng, cần toán tửeq.
- Nút xóa gửi yêu cầu xóa to-do tương ứng bằng
hx-post="/rpc/delete_todo" api.html_editable_task(_id int)trả về HTML trường nhập liệu để chỉnh sửa một to-do cụ thể- Gửi tên tác vụ đã thay đổi bằng
hx-post="/rpc/change_todo_task" - Cập nhật khi submit hoặc mất focus bằng
hx-trigger="submit,focusout" - Bao gồm
hx-headers='{"Accept": "text/html"}'
- Gửi tên tác vụ đã thay đổi bằng
- Tất cả endpoint thay đổi dữ liệu đều trả về HTML danh sách đầy đủ sau khi thay đổi
api.change_todo_state(_id int, _done boolean): cập nhật cộtdoneapi.change_todo_task(_id int, _task text): cập nhật cộttaskapi.delete_todo(_id int): xóa to-do tương ứng với_id
- Sau khi làm mới schema cache, có thể chỉnh sửa, xóa và đánh dấu hoàn thành to-do tại
http://localhost:3000/rpc/index
1 bình luận
Ý kiến trên Hacker News
PostgREST là một trong những dự án mã nguồn mở mình thích nhất. Mình nghĩ việc Supabase trở thành công ty trị giá 1 tỷ USD phần lớn là nhờ thiết kế tuyệt vời của PostgREST và Postgres
Mình không biết Supabase tài trợ cho dự án này như thế nào, nhưng hy vọng là ở quy mô rất lớn. Thật buồn khi nhìn vào thực tế tài trợ cho mã nguồn mở: một dự án được ít nhất hàng trăm công ty có doanh thu dùng làm phụ thuộc cốt lõi mà chỉ có 12 người tài trợ trả phí
https://www.patreon.com/postgrest/about
https://github.com/steve-chavez
Vì vậy giờ mình chỉ dùng AGPLv3 hoặc giấy phép tương tự. Kiểu nếu muốn dùng thương mại thì trả 1% tổng doanh thu
Là một bản chứng minh khái niệm thì rất hay và phần triển khai cũng đáng khen, nhưng để bảo trì trong một web app không hề tầm thường thì trông như ác mộng
https://sqitch.org/
Mình tò mò liệu những tính năng hoặc mẫu lập trình như thế này có mới không, hoặc có được dùng trong các ứng dụng hiện đại không
CouchDB, một cơ sở dữ liệu tài liệu JSON, từng cung cấp API dựa trên HTTP và có sẵn các phương thức list/detail có thể trả về bất kỳ định dạng nào tạo được bên trong trình thông dịch JavaScript. Tức là client có thể gọi trực tiếp cơ sở dữ liệu để nhận HTML hoặc JSON, không cần server
Nhưng sau v1 họ đã dừng hướng đó, vì việc bảo trì trở thành ác mộng. Chắc nhiều người còn nhớ “ngày xưa tốt đẹp” khi trong một file PHP hoặc ASP vừa có câu lệnh SQL vừa có HTML; cái này trông chẳng khác mấy
Khoảng 13 năm trước, trong một dự án cá nhân, mình thật sự thích các tính năng liên quan đến web của CouchDB, nhưng với một team thì xử lý khá bất tiện
Ứng dụng PostgREST duy nhất mình từng làm việc cùng thật kinh khủng. Vì cũng như đa số các framework “đơn giản” kiểu này, chúng chỉ đơn giản cho đến khi yêu cầu trở nên phức tạp
Những tác giả ban đầu cuối cùng phải dùng cả đống stored procedure trong cơ sở dữ liệu để đạt kết quả mong muốn, và điều đó dẫn đến vấn đề về khả năng mở rộng. Như mọi khi, giải pháp là quay lại với SQL
PostgREST được tạo ra cho ứng dụng CRUD, và nhiều ứng dụng mình gặp thuộc loại này. Logic backend tùy chỉnh thỉnh thoảng cần đến thì có thể xử lý bằng cách chạy server riêng hoặc edge function
Cũng như mọi công cụ kiểu này, khi gặp yêu cầu thực tế, chi phí thích nghi theo công cụ còn tệ hơn thứ mà nó định thay thế, tức SQL + một ngôn ngữ và framework nào đó. PostGREST hiện cũng đã trở nên phức tạp rồi, nên những tính năng bổ sung như thế này làm nó kém hấp dẫn hơn với mình
Trước khi Rails xuất hiện và khiến mọi người tin rằng đưa business logic vào các ngôn ngữ phía server chậm chạp là ý hay, ai cũng xây app theo cách này
Đây là một stack phát triển web thật sự gọn gàng. Chỉ có HTML và cơ sở dữ liệu, không cần backend và frontend
https://github.com/omnigres/omnigres
Khi ánh hào quang của HTMX phai đi, người bảo trì lúc đó cuối cùng sẽ phải thừa nhận rằng cần viết lại tất cả từ đầu
Trong một dự án cũ, mình đã dùng PostgREST mà không có HTMX, và khá ấn tượng với việc có thể đẩy nó đi xa đến mức nào
HTMX cũng có vẻ hợp, nhưng mình không chắc mình muốn bảo trì template HTMX bên trong các hàm SQL đến mức nào
Làm để nghịch thì tốt, nhưng không phù hợp để xây thứ ổn định
Nhìn từ góc độ Haskell, PostgREST rất thú vị. Vì nó trông như một dự án quá hiển nhiên
Nhưng chính vì vậy mà nó thiên tài. Mình thích vì đó là một ý tưởng rất đúng chất Haskell
Theo bạn, cần thêm những công cụ nào để biến khái niệm này thành một stack có thể bảo trì với trải nghiệm người dùng tốt cho các ứng dụng quy mô vừa và lớn?
Tôi cũng từng định xây một thứ tương tự trên SQL; stack kỹ thuật là một lớp OpenAPI được triển khai bằng SQL template Jinja và YML, nhưng có lẽ vẫn sẽ giới hạn trong phạm vi công cụ nội bộ. Nó ở đây: https://jinj.at
Show HN liên quan: render HTML từ SQL bằng pg_render
https://news.ycombinator.com/item?id=38677852
Tôi muốn ghi lại một tác vụ trong app này để khỏi quên. Tác vụ của tôi là:
Trong trường hợp này, có vẻ cột task không được làm sạch ở đâu cả.
Nhưng giờ database lại là view, nên XSS quay lại sao? Đây là vấn đề có thể ngăn chặn được.
HTML có thể được render bằng bất kỳ ngôn ngữ template nào có xử lý việc đó.