@veap/commentable adds polymorphic threaded comments to any model. Comments live in a shared comments table and support logged-in authors or guest comments, nested replies, and moderation statuses.
Install and migrate#
import { runMigrations } from "@veap/database";
import { migrations as commentableMigrations } from "@veap/commentable/migrations";
await runMigrations("app", commentableMigrations);Make a model commentable#
import { CommentableModel } from "@veap/commentable";
export class BlogPost extends CommentableModel {
static override table = "blog_posts";
static override morphAlias = "post"; // optional explicit polymorphic type
}Or via the mixin:
import { Commentable } from "@veap/commentable";
import { TranslatableModel } from "@veap/translatable";
export class Article extends Commentable(TranslatableModel) {
static override table = "articles";
}Add and read comments#
await post.addComment({ content: "Great article!", authorId: user.id });
await post.addComment({
content: "Thanks!",
authorName: "Jane",
authorEmail: "jane@example.com",
});
await post.addComment({ content: "A reply", parentId: parentComment.id });
const approved = await post.approvedComments();
const all = await post.comments().get();New comments default to status: "approved". Pass status: "pending" (or another status) to place a comment in moderation.
Replies#
const reply = await parentComment.reply({ content: "A nested reply", authorId: user.id });Comment also exposes parent() and replies() relations for building the thread tree.
Query#
const commented = await BlogPost.query().whereHasComments().get();
const withComments = await BlogPost.query().withComments().get();UI#
@veap/commentable/ui exports CommentSection (threaded display) and CommentForm (submission). They are presentational client components: pass already-loaded comments and an onAddComment/onSubmit handler, typically a Server Action. The section renders nested replies and offers a reply control when allowReplies is set.
Moderation and security#
- A
statusvalue is data, not authorization. Enforce who may comment, reply, or moderate with server-side checks in your action before writing. - Guest comments accept
authorName/authorEmail; validate and length-limit them, and never treat them as verified identity. addCommentandreplysave the comment directly rather than throughtransaction(). If your application requires transactional writes, wrap the call yourself.- Render comment bodies as plain text or sanitize them before rendering as HTML/Markdown.