Docs/Guides/ORM traits

Taggable

@veap/taggable adds polymorphic, translatable tags to any model. Tags live in a shared tags table and are attached through a taggables pivot.

2 min read

@veap/taggable adds polymorphic, translatable tags to any model. Tags live in a shared tags table and are attached through a taggables pivot.

Install and migrate#

Run the migrations for @veap/translatable first (tag names/slugs are translated), then taggable:

ts
import { runMigrations } from "@veap/database";
import { migrations as taggableMigrations } from "@veap/taggable/migrations";

await runMigrations("app", taggableMigrations);

Make a model taggable#

ts
import { TaggableModel } from "@veap/taggable";

export class BlogPost extends TaggableModel {
  static override table = "blog_posts";
}

Or, when your model already extends another base class:

ts
import { Taggable } from "@veap/taggable";
import { TranslatableModel } from "@veap/translatable";

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

Attach, detach and sync#

ts
await post.attachTags(["typescript", "veap"]);
await post.attachTags(tagInstance);        // attach a Tag instance
await post.detachTags("veap");
await post.detachTags();                   // detach all
await post.syncTags(["orm", "knex"]);      // replace the whole set

String arguments are resolved by Tag.findOrCreateByName, which reuses an existing tag matched by translated name or slug and creates one otherwise. A UUID-looking string is treated as an existing tag id.

Query by tag#

ts
const posts = await BlogPost.query().whereTag("typescript", "en").get();
const any = await BlogPost.query().whereAnyTag(["orm", "knex"], "en").get();
const all = await BlogPost.query().whereAllTags(["orm", "knex"], "en").get();

const withTags = await BlogPost.query().withTags().get(); // eager-loads tags.translations

UI#

@veap/taggable/ui exports TagInput, a client input for entering and suggesting tag names. value accepts names or Tag objects; onChange receives string[]. Use availableTags for suggestions and maxTags to cap the count.

Caveats#

  • The tag helpers default to the pl locale; pass the locale explicitly when your content is not Polish.
  • attachTags/syncTags create missing tags as a side effect. Validate and normalize user input before persisting it, and consider a curation step so the tag table does not accumulate near-duplicates.

See also#

veap
Preparing0%