@nestbolt/taggable
Quick Start
Register the module, decorate an entity, and start tagging in under five minutes.
This guide walks you through the minimal setup to start tagging entities.
1. Register the Module
Add TaggableModule.forRoot() to your root module. The module is registered globally, so you only need to import it once:
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { TaggableModule } from "@nestbolt/taggable";
import { Post } from "./entities/post.entity";
@Module({
imports: [
TypeOrmModule.forRoot({
type: "postgres",
// ... your database config
entities: [Post],
synchronize: true,
}),
TaggableModule.forRoot(),
],
})
export class AppModule {}2. Mark Your Entity as Taggable
Add the @Taggable() decorator to your entity. Optionally extend TaggableMixin for convenience methods:
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;
}3. Create and Attach Tags
Inject TaggableService and start tagging:
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { TaggableService } from "@nestbolt/taggable";
import { Post } from "./entities/post.entity";
@Injectable()
export class PostService {
constructor(
@InjectRepository(Post) private readonly postRepo: Repository<Post>,
private readonly taggableService: TaggableService,
) {}
async createAndTag(title: string, tagNames: string[]) {
const post = await this.postRepo.save(this.postRepo.create({ title }));
for (const name of tagNames) {
const tag = await this.taggableService.findOrCreateTag(name);
await this.taggableService.attachTag(Post, post.id, tag.id);
}
return post;
}
}4. Query Tags on an Entity
// Get all tags for a post
const tags = await this.taggableService.getEntityTags(Post, postId);
console.log(tags.map(t => t.name)); // ["NestJS", "TypeORM"]
// Check if a post has a specific tag
const hasTag = await this.taggableService.hasTag(Post, postId, tag.id);
// Count tags
const count = await this.taggableService.getTagCount(Post, postId);5. Use the Mixin Methods
If your entity extends TaggableMixin, you can call tag methods directly on entity instances:
const post = await this.postRepo.findOneBy({ id: postId });
await post.attachTag(tag.id);
await post.detachTag(tag.id);
await post.syncTags([tagId1, tagId2]);
const tags = await post.getTags();
const hasTag = await post.hasTag(tagId);
const count = await post.getTagCount();6. Sync Tags
Replace all tags on an entity with a new set -- missing tags are attached, extra tags are removed:
await this.taggableService.syncTags(Post, postId, [tagId1, tagId2, tagId3]);What Happened Behind the Scenes
TaggableModule.forRoot()registeredTaggableServiceglobally and created thetagsandtaggablestables via TypeORM.- The
@Taggable()decorator stored metadata on thePostclass, mapping it to the type"Post"in the pivot table. findOrCreateTag()looked up existing tags by slug and created new ones as needed.attachTag()created a row in thetaggablespivot table linking the tag to the post.- Query methods read from the pivot table with the correct
taggableTypefilter, so tags for different entity types never mix.
Next Steps
- Configuration --
forRootandforRootAsyncmodule setup. - Decorator -- customize the entity type name.
- Mixin -- all mixin methods explained.
- Tag CRUD -- create tags with types, metadata, and custom slugs.
- Events -- listen to tag lifecycle events.