@nestbolt/taggable
Entity Mixin
TaggableMixin adds tag methods directly to your entity instances.
TaggableMixin
TaggableMixin is an optional mixin that adds convenience methods directly to your entity instances. Instead of calling TaggableService with entity class and ID arguments, you call methods directly on the entity:
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm";
import { Taggable, TaggableMixin } from "@nestbolt/taggable";
@Taggable()
@Entity("posts")
export class Post extends TaggableMixin(BaseEntity) {
@PrimaryGeneratedColumn("uuid")
id!: string;
@Column()
title!: string;
}Methods
| Method | Returns | Description |
|---|---|---|
attachTag(tagId) | Promise<void> | Attach a tag to this entity |
detachTag(tagId) | Promise<void> | Detach a tag from this entity |
syncTags(tagIds) | Promise<void> | Sync tags -- adds missing, removes extra |
getTags() | Promise<TagEntity[]> | Get all tags on this entity |
hasTag(tagId) | Promise<boolean> | Check if this entity has a specific tag |
getTagCount() | Promise<number> | Count tags on this entity |
getTaggableType() | string | Get the entity type name |
getTaggableId() | string | Get the entity ID |
Usage Examples
const post = await postRepo.findOneBy({ id: postId });
// Attach and detach
await post.attachTag(tagId);
await post.detachTag(tagId);
// Sync -- replaces all tags with the given set
await post.syncTags([tagId1, tagId2, tagId3]);
// Query
const tags = await post.getTags();
const hasNestJS = await post.hasTag(nestjsTagId);
const count = await post.getTagCount();
// Type info
console.log(post.getTaggableType()); // "Post"
console.log(post.getTaggableId()); // "abc-123-..."Composing with Other Mixins
TaggableMixin can be composed with other mixins by nesting them:
import { TaggableMixin } from "@nestbolt/taggable";
import { SluggableMixin } from "@nestbolt/sluggable";
import { HasMediaMixin } from "@nestbolt/medialibrary";
@Taggable()
@Sluggable({ from: "title" })
@Entity("posts")
export class Post extends TaggableMixin(SluggableMixin(HasMediaMixin(BaseEntity))) {
@PrimaryGeneratedColumn("uuid")
id!: string;
@Column()
title!: string;
@Column({ default: "" })
slug!: string;
}The order of nesting doesn't matter -- each mixin adds its own methods independently.
Error Handling
If you call a mixin method before TaggableModule has been initialized, a TaggableNotInitializedException is thrown:
import { TaggableNotInitializedException } from "@nestbolt/taggable";
try {
await post.attachTag(tagId);
} catch (error) {
if (error instanceof TaggableNotInitializedException) {
console.log(error.message);
// "TaggableModule has not been initialized. Make sure TaggableModule.forRoot() is imported."
}
}This typically happens when:
TaggableModule.forRoot()was not imported in your root module- You're calling mixin methods outside the NestJS application lifecycle (e.g., in a standalone script without bootstrapping the module)
Without the Mixin
The mixin is entirely optional. You can use TaggableService directly for all operations:
// With mixin
await post.attachTag(tagId);
// Without mixin (equivalent)
await taggableService.attachTag(Post, post.id, tagId);The mixin simply delegates to TaggableService internally, using the entity's constructor and ID.