@molecule/api-slug
Utility · slug · API (Node) · v1.0.1 · Apache-2.0
URL slugs from titles and names: accent-folding slugify, and unique slugs checked against your store (a table column or any lookup).
npm install @molecule/api-slugHow it works
@molecule/api-slug is a utility package for the API (Node) side (slug).
import { slugify, uniqueSlugInTable } from '@molecule/api-slug'
slugify('Café au lait: a how-to!') // 'cafe-au-lait-a-how-to'
// In a create handler: the first "Hello World" gets hello-world, the next hello-world-2.
const slug = await uniqueSlugInTable('articles', 'slug', input.title)
// On update, a row keeps its own slug:
const next = await uniqueSlugInTable('articles', 'slug', input.title, { excludeId: article.id })Reference
URL slugs for titles and names.
slugify folds accents and punctuation into a lowercase, hyphenated slug;
uniqueSlug finds one your store does not have yet; uniqueSlugInTable
does that against a table column through the bonded DataStore.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { slugify, uniqueSlugInTable } from '@molecule/api-slug'
slugify('Café au lait: a how-to!') // 'cafe-au-lait-a-how-to'
// In a create handler: the first "Hello World" gets hello-world, the next hello-world-2.
const slug = await uniqueSlugInTable('articles', 'slug', input.title)
// On update, a row keeps its own slug:
const next = await uniqueSlugInTable('articles', 'slug', input.title, { excludeId: article.id })
Type
utility
Installation
npm install @molecule/api-slug @molecule/api-database
API
Interfaces
SlugifyOptions
How slugify shapes a slug.
interface SlugifyOptions {
/** Joins the words (default `'-'`). */
separator?: string
/** Longest slug kept, cut at a word boundary when one is near (default 80). */
maxLength?: number
}
UniqueSlugOptions
How uniqueSlug looks for a free slug.
interface UniqueSlugOptions extends SlugifyOptions {
/** Numbered tries (`slug-2` … `slug-N`) before a random suffix (default 20). */
maxNumbered?: number
/** Always end in a random suffix, even when the plain slug is free (default false). */
alwaysSuffix?: boolean
/** The base when the text has no letters or digits (default `'item'`). */
fallback?: string
}
Functions
slugify(text, options)
Turn a title or name into a URL slug: accents folded ("Café" → "cafe"),
lowercase, every run of other characters one separator, no separator at
either end, cut to maxLength at a word boundary when one is near.
function slugify(text: string, options?: SlugifyOptions): string
text— The title or name.options— Separator and maximum length.
Returns: The slug; empty when the text has no letters or digits at all.
uniqueSlug(text, isTaken, options)
A slug for text that isTaken says is free: the plain slug, then
slug-2, slug-3 … up to maxNumbered, then slug-<random>.
A check-then-insert can still race another writer: keep a UNIQUE index on the column and retry the insert on a conflict (call this again).
function uniqueSlug(
text: string,
isTaken: (slug: string) => Promise<boolean> | boolean,
options?: UniqueSlugOptions,
): Promise<string>
text— The title or name.isTaken— Whether a slug is already used (your store's lookup).options— Slug options, the numbered tries and a fallback for text with no letters.
Returns: A slug isTaken reported free.
uniqueSlugInTable(table, column, text, options)
uniqueSlug checked against a table column through the bonded
DataStore (@molecule/api-database).
function uniqueSlugInTable(
table: string,
column: string,
text: string,
options?: UniqueSlugOptions & { excludeId?: string },
): Promise<string>
table— The table, e.g.'articles'.column— The slug column, e.g.'slug'.text— The title or name.options— As foruniqueSlug;excludeIdlets a row keep its own slug on update.
Returns: A slug no other row of the table uses.
Injection Notes
Requirements
Peer dependencies:
@molecule/api-database^1.0.1
Runtime Dependencies
-
@molecule/api-database -
Keep a UNIQUE index on the slug column. Checking and then inserting can race another request; the index makes the second insert fail, and calling
uniqueSlugInTableagain finds the next free slug. -
alwaysSuffix: truegives every row a random tail (hello-world-k3x9q2), for specs that expect slugs not to collide or not to be guessable. -
Text with no letters or digits (
'!!!') slugifies to''; the unique variants fall back tofallback(default'item'). -
uniqueSlugInTableneeds a DataStore bond (@molecule/api-database);slugifyanduniqueSlugneed nothing.