NestboltNestbolt

@nestbolt/sluggable

Configuration

All SluggableModule.forRoot() and forRootAsync() options explained.

forRoot Options

SluggableModule.forRoot() accepts an optional SluggableModuleOptions object to set global defaults:

SluggableModule.forRoot({
  separator: "-",
  maxLength: 255,
  lowercase: true,
  transliterate: true,
  onUpdate: "keep",
  suffixSeparator: "-",
})
OptionTypeDefaultDescription
separatorstring"-"Word separator in generated slugs
maxLengthnumber255Maximum slug length. Truncates at word boundaries
lowercasebooleantrueWhether to lowercase the slug
transliteratebooleantrueTransliterate non-Latin characters to ASCII
transliterator(input: string) => stringBuilt-inCustom transliteration function
onUpdate"keep" | "regenerate""keep"Default update behavior for all entities
suffixSeparatorstring"-"Separator between base slug and collision suffix

All options are optional -- calling SluggableModule.forRoot() with no arguments uses all defaults.

forRootAsync

Use forRootAsync() when your configuration depends on other providers (e.g., ConfigService):

import { ConfigModule, ConfigService } from "@nestjs/config";
import { SluggableModule } from "@nestbolt/sluggable";

@Module({
  imports: [
    ConfigModule.forRoot(),
    SluggableModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        maxLength: config.get<number>("SLUG_MAX_LENGTH", 100),
        onUpdate: config.get<string>("SLUG_ON_UPDATE", "keep") as "keep" | "regenerate",
      }),
    }),
  ],
})
export class AppModule {}

forRootAsync Options

OptionTypeDescription
importsany[]Modules to import for dependency injection
injectany[]Providers to inject into the factory function
useFactory(...args) => SluggableModuleOptionsFactory function that returns the options

Option Details

separator

Controls the character used between words in the generated slug:

SluggableModule.forRoot({ separator: "_" })
// "Hello World" → "hello_world"

SluggableModule.forRoot({ separator: "-" })
// "Hello World" → "hello-world"

This can be overridden per entity via the @Sluggable() decorator.

maxLength

Slugs longer than maxLength are truncated at the last word boundary before the limit:

SluggableModule.forRoot({ maxLength: 20 })
// "This is a very long article title" → "this-is-a-very-long"

Truncation never splits a word or leaves a trailing separator.

transliterate

When enabled, non-Latin characters are converted to their ASCII equivalents before slugifying:

// transliterate: true (default)
// "café résumé" → "cafe-resume"

// transliterate: false
// "café résumé" → "caf-rsum"

See Transliteration for details on supported character sets and custom transliterators.

onUpdate

Controls the global default for what happens when an entity is updated:

  • "keep" (default) -- the slug is preserved unless it's empty
  • "regenerate" -- the slug is regenerated from source fields when they change

This can be overridden per entity via the @Sluggable() decorator. See Update Behavior for details.

suffixSeparator

Controls the separator between the base slug and the collision suffix number:

SluggableModule.forRoot({ suffixSeparator: "_" })
// First: "hello-world"
// Collision: "hello-world_1"

SluggableModule.forRoot({ suffixSeparator: "-" })
// First: "hello-world"
// Collision: "hello-world-1"

SluggableService API

SluggableService is available via dependency injection after module registration:

@Injectable()
export class MyService {
  constructor(private readonly sluggable: SluggableService) {}
}

generateSlug(input, overrides?)

Generates a slug from a string without uniqueness checking:

sluggable.generateSlug("Hello World");
// "hello-world"

sluggable.generateSlug("Hello World", { separator: "_", maxLength: 10 });
// "hello_worl"

generateUniqueSlug(entityConstructor, slugField, baseSlug, excludeId?)

Generates a unique slug by checking the database for collisions:

const slug = await sluggable.generateUniqueSlug(Post, "slug", "hello-world");
// "hello-world" or "hello-world-1" if collision exists

Pass excludeId to exclude the current entity during update:

const slug = await sluggable.generateUniqueSlug(Post, "slug", "hello-world", post.id);

findBySlug(entityConstructor, slugField, slug)

Finds an entity by its slug value:

const post = await sluggable.findBySlug(Post, "slug", "hello-world");
// Post | null

regenerateSlug(entity, sourceFields, slugField, overrides?)

Regenerates the slug for an entity from its current source field values:

post.title = "New Title";
const newSlug = await sluggable.regenerateSlug(post, ["title"], "slug");
// "new-title"