NestboltNestbolt

@nestbolt/sluggable

Decorator

@Sluggable() decorator options for per-entity slug configuration.

The @Sluggable() decorator is applied to your TypeORM entity class to enable automatic slug generation. It stores configuration metadata that the SluggableSubscriber reads on insert and update.

Basic Usage

import { Sluggable } from "@nestbolt/sluggable";

@Sluggable({ from: "title" })
@Entity("posts")
export class Post {
  @PrimaryGeneratedColumn("uuid")
  id!: string;

  @Column()
  title!: string;

  @Column({ default: "" })
  slug!: string;
}

Options Reference

OptionTypeDefaultDescription
fromstring | string[](required)Source field(s) to generate the slug from
slugFieldstring"slug"Entity column to store the generated slug
separatorstringModule default ("-")Word separator for this entity
maxLengthnumberModule default (255)Maximum slug length for this entity
onUpdate"keep" | "regenerate"Module default ("keep")Update behavior for this entity
uniquebooleantrueEnable unique slug collision handling

Source Fields

Single Field

@Sluggable({ from: "title" })
// { title: "Hello World" } → "hello-world"

Multiple Fields

Pass an array to concatenate multiple fields:

@Sluggable({ from: ["firstName", "lastName"] })
// { firstName: "John", lastName: "Doe" } → "john-doe"

Fields are joined with a space before slugifying. Empty or falsy fields are filtered out:

@Sluggable({ from: ["firstName", "middleName", "lastName"] })
// { firstName: "John", middleName: "", lastName: "Doe" } → "john-doe"

Custom Slug Field

By default, the slug is stored in the slug column. Use slugField to change it:

@Sluggable({ from: "title", slugField: "permalink" })
@Entity("articles")
export class Article {
  @PrimaryGeneratedColumn("uuid")
  id!: string;

  @Column()
  title!: string;

  @Column({ default: "" })
  permalink!: string;
}

Per-Entity Separator and Max Length

Override the global separator and max length for specific entities:

@Sluggable({ from: "name", separator: "_", maxLength: 30 })
@Entity("categories")
export class Category {
  @PrimaryGeneratedColumn("uuid")
  id!: string;

  @Column()
  name!: string;

  @Column({ default: "" })
  slug!: string;
}

// { name: "Electronics & Gadgets" } → "electronics_gadgets"

Disabling Unique Slugs

By default, slug uniqueness is enforced with collision detection. Set unique: false to disable it:

@Sluggable({ from: "title", unique: false })
@Entity("tags")
export class Tag {
  @PrimaryGeneratedColumn("uuid")
  id!: string;

  @Column()
  title!: string;

  @Column({ default: "" })
  slug!: string;
}

// Two tags with title "JavaScript" both get slug "javascript" -- no suffix

See Unique Slugs for more details.

Update Behavior

Control what happens when the entity is updated:

@Sluggable({ from: "title", onUpdate: "regenerate" })
@Entity("pages")
export class Page {
  // ...
}
  • "keep" -- slug is preserved on update (unless it's empty)
  • "regenerate" -- slug is regenerated when source fields change

See Update Behavior for full details.

Skip Conditions

The subscriber skips slug generation in these cases:

  1. Slug already set -- if the slug field has a truthy value on insert, the subscriber leaves it as-is. This lets you set custom slugs manually.
  2. Source fields empty -- if all source fields are empty or falsy, no slug is generated.
  3. No decorator -- entities without @Sluggable() are ignored by the subscriber.