Xây Dựng Blog Này Bằng Next.js 16 và Supabase
Blog bạn đang đọc chạy trên cùng một stack với phần còn lại của portfolio: Next.js 16 App Router, React 19 và backend Supabase Postgres. Bài viết này đi qua cách các phần ghép với nhau.
Mô hình nội dung
Bài viết nằm trong hai bảng: một dòng blog_posts trung lập ngôn ngữ cùng các dòng blog_post_translations cho từng ngôn ngữ. Markdown được lưu thô và render khi có yêu cầu, nên bản tiếng Anh và tiếng Việt dùng chung slug, chuyên mục và ảnh bìa.
Vì sao chọn dòng dịch thay vì cột JSON
Cột có kiểu rõ ràng giữ cho truy vấn trung thực. Thiếu bản dịch tiếng Việt chỉ khiến bài bị ẩn khỏi danh sách tiếng Việt thay vì hiển thị nửa dịch nửa chưa dịch.
Đường đọc dữ liệu
Một module repository duy nhất sở hữu mọi truy vấn công khai. Nó map dòng dữ liệu thành view model, tính thời gian đọc từ thân markdown và không bao giờ để lộ dòng dữ liệu thô ra component.
const listing = await getPublishedPosts(locale, page);
Danh sách được bọc trong cache có tag để mọi chỉnh sửa từ admin revalidate thông qua đúng một tag.
Kết luận
- Định nghĩa bản dịch thành dòng, không phải document
- Render markdown phía server, trả HTML về client
- Một accessor có cache cho mỗi đường đọc giúp việc vô hiệu hóa đơn giản


