@nestbolt/sluggable
Unique Slugs
How slug collision handling works and how to customize or disable it.
By default, @nestbolt/sluggable ensures every slug is unique within its entity table. When a collision is detected, a numeric suffix is appended automatically.
How It Works
When generating a slug, the subscriber:
- Generates the base slug from source fields (e.g.,
"hello-world") - Checks if that slug already exists in the database
- If no collision, uses the base slug as-is
- If a collision exists, finds all matching slugs with numeric suffixes (e.g.,
hello-world-1,hello-world-2) - Appends the next number:
hello-world-3
await repo.save(repo.create({ title: "Hello World" }));
// slug: "hello-world"
await repo.save(repo.create({ title: "Hello World" }));
// slug: "hello-world-1"
await repo.save(repo.create({ title: "Hello World" }));
// slug: "hello-world-2"Suffix Separator
The separator between the base slug and the suffix number defaults to "-". Customize it globally:
SluggableModule.forRoot({ suffixSeparator: "_" })
// "hello-world", "hello-world_1", "hello-world_2"Excluding the Current Entity
When regenerating a slug on update, the current entity is excluded from the collision check so it doesn't collide with itself:
// post.slug is currently "hello-world"
post.title = "Hello World"; // same title, regenerate
await repo.save(post);
// slug stays "hello-world" -- the entity doesn't collide with its own slugThis works automatically in the subscriber. When using SluggableService.generateUniqueSlug() directly, pass the entity ID:
const slug = await sluggable.generateUniqueSlug(
Post,
"slug",
"hello-world",
post.id, // exclude this entity from collision check
);Disabling Unique Slugs
Set unique: false on the @Sluggable() decorator to skip collision checking entirely:
@Sluggable({ from: "title", unique: false })
@Entity("tags")
export class Tag {
@PrimaryGeneratedColumn("uuid")
id!: string;
@Column()
title!: string;
@Column({ default: "" })
slug!: string;
}With unique: false:
- No database queries are made to check for collisions
- Multiple entities can have the same slug
- Useful for tags, categories, or cases where slug uniqueness is not required
Manual Slugs
If you set the slug field before saving, the subscriber skips generation entirely:
const post = repo.create({ title: "Hello World", slug: "custom-slug" });
await repo.save(post);
console.log(post.slug); // "custom-slug" -- no auto-generationThis means manually-set slugs bypass collision checking. If you need uniqueness for manual slugs, check it yourself or use SluggableService.generateUniqueSlug().