RealWorld API Slug Generation: Algorithm and Implementation

RealWorld API generates article slugs by normalizing the article title with the slugify library and appending the author's numeric user ID to guarantee uniqueness.

The RealWorld API, maintained in the gothinkster/realworld repository, implements a deterministic slug generation strategy that transforms article titles into URL-friendly identifiers. This RealWorld API slug generation process combines third-party text normalization with a per-author identifier suffix to prevent collisions across the platform.

RealWorld API Slug Generation Algorithm

The slug creation process follows a deterministic two-stage pipeline: first normalizing the title string, then appending a unique author identifier.

Title Normalization with the slugify Library

The backend imports the third-party slugify npm package (version 1.6.0) as declared in apps/api/package.json. According to the source code, this library applies three transformations by default: converting the string to lowercase, replacing any run of non-alphanumeric characters with a single hyphen (-), and trimming leading or trailing hyphens.

// Simplified view of slugify's algorithm (default options)
// 1) toLowerCase()
// 2) replace(/[^a-z0-9]+/g, '-')
// 3) trim(/^[-]+|[-]+$/g, '')

Author ID Suffix for Global Uniqueness

To guarantee uniqueness even when two users create articles with identical titles, the service appends a hyphen and the authenticated user's numeric ID (auth.id). The composite format follows this pattern:

const slug = `${slugify(title)}-${auth.id}`;

This ensures that my-article-title-1 and my-article-title-2 are distinct slugs belonging to different authors.

Database Uniqueness Validation

Before persisting a new article, the API queries the database for an existing record with the same slug. If found, the server returns a 422 error indicating the title must be unique. During article updates, the system recomputes the slug only if the title field changes, maintaining stable URLs for unchanged content.

Route Handler Implementation

All slug generation logic lives in the article route handlers within the apps/api directory, with specific implementations for creation and update operations.

Article Creation in index.post.ts

In apps/api/server/routes/api/articles/index.post.ts at line 24, the POST handler constructs the slug by combining the normalized title with the author ID extracted from the authentication context.

// ----------------------------------------------------
// 1️⃣ Create an article – slug generation (POST /api/articles)
// ----------------------------------------------------
import slugify from 'slugify';
import { definePrivateEventHandler } from '~/auth-event-handler';

export default definePrivateEventHandler(async (event, { auth }) => {
  const { article } = await readBody(event);
  const { title, description, body, tagList } = article;

  // ---- slug generation ----
  //   1. Normalise title with slugify (lower‑case, non‑alphanum → '-')
  //   2. Append author id to guarantee uniqueness
  const slug = `${slugify(title)}-${auth.id}`;

  // …store article in DB using `slug`…
});

Conditional Regeneration in index.put.ts

In apps/api/server/routes/api/articles/[slug]/index.put.ts at line 37, the PUT handler conditionally regenerates the slug only when the request body includes a modified title. This prevents breaking existing article URLs when updating only the body or description.

// ----------------------------------------------------
// 2️⃣ Update an article – conditional slug regeneration (PUT /api/articles/:slug)
// ----------------------------------------------------
import slugify from 'slugify';
import { definePrivateEventHandler } from '~/auth-event-handler';

export default definePrivateEventHandler(async (event, { auth }) => {
  const { article } = await readBody(event);
  const currentSlug = getRouterParam(event, 'slug');

  let newSlug: string | null = null;
  if (article.title) {
    // Regenerate slug only if title changed
    newSlug = `${slugify(article.title)}-${auth.id}`;
  }

  // …apply `newSlug` (if any) when updating the DB…
});

Key Source Files and Dependencies

The RealWorld API slug generation relies on the following specific files in the gothinkster/realworld repository:

Summary

  • The RealWorld API uses the slugify library (v1.6.0) to normalize article titles by converting to lowercase and replacing non-alphanumeric characters with hyphens.
  • The algorithm appends the author's numeric user ID (auth.id) to the normalized title to guarantee uniqueness across the platform.
  • The database validates slug uniqueness before persistence, returning a 422 error if collisions are detected.
  • During updates, slugs are only regenerated if the article title changes, preserving URL stability for other modifications.
  • All logic is implemented in the route handlers at apps/api/server/routes/api/articles/index.post.ts and [slug]/index.put.ts.

Frequently Asked Questions

What npm package does RealWorld use for slug generation?

The RealWorld API uses the slugify package version 1.6.0, as specified in apps/api/package.json. This library handles the conversion of titles to lowercase and replaces sequences of non-alphanumeric characters with single hyphens.

How does RealWorld ensure article slugs are unique?

The API appends the author's numeric user ID to the normalized title string, creating a composite slug like my-article-title-123. Additionally, the system queries the database before persistence and returns a 422 error if a duplicate slug already exists for that author.

Does updating an article always change its slug in RealWorld?

No, the slug is only regenerated during PUT requests when the request body includes a title field. As implemented in apps/api/server/routes/api/articles/[slug]/index.put.ts, the handler checks for the presence of article.title before computing a new slug value, ensuring existing URLs remain valid when updating other fields.

What happens if two different users create articles with identical titles?

Because the slug format includes the user ID suffix (slugify(title)-auth.id), two different users will generate different slugs for identical titles (e.g., my-title-1 vs my-title-2). However, if the same user attempts to create multiple articles with the same title, the database uniqueness check will reject the second attempt with a 422 error.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →