Docs/Guides/ORM traits

Commentable

@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…

2 min read

@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#

ts
import { runMigrations } from "@veap/database";
import { migrations as commentableMigrations } from "@veap/commentable/migrations";

await runMigrations("app", commentableMigrations);

Make a model commentable#

ts
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:

ts
import { Commentable } from "@veap/commentable";
import { TranslatableModel } from "@veap/translatable";

export class Article extends Commentable(TranslatableModel) {
  static override table = "articles";
}

Add and read comments#

ts
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#

ts
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#

ts
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 status value 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.
  • addComment and reply save the comment directly rather than through transaction(). If your application requires transactional writes, wrap the call yourself.
  • Render comment bodies as plain text or sanitize them before rendering as HTML/Markdown.

See also#

veap
Preparing0%