Generate Product Slugs Automatically: Complete 2026 Guide

Backfill missing product slugs across locales (sl, en, ru) using a safe Payload job, API trigger, and migration steps

·Updated on:·Matija Žiberna·
Generate Product Slugs Automatically: Complete 2026 Guide

📚 Comprehensive Payload CMS Guides

Detailed Payload guides with field configuration examples, custom components, and workflow optimization tips to speed up your CMS development process.

No spam. Unsubscribe anytime.

Generating Product Slugs Automatically

This guide explains how to use the automated product slug generation system to populate missing slugs for all products across all locales (Slovenian, English, Russian).

Overview

The slug generation task automatically creates URL-friendly slugs for products based on their titles. It:

  • Generates slugs from titles: Uses the same logic as manual slug creation (src/utilities/formatSlug.ts)
  • Respects existing slugs: Only generates slugs for products/locales that don't have one yet
  • Handles all locales: Generates localized slugs for sl, en, and ru based on their respective titles
  • Safe operation: Never overwrites existing slugs

How It Works

The slug generation process:

  1. Fetches all products from the database with all locales
  2. For each product, checks each locale (sl, en, ru)
  3. If a slug is missing for that locale:
    • Gets the product title for that locale
    • Generates a URL-friendly slug using the same formatting rules as manual entry
    • Updates only that specific locale's slug field
  4. Provides detailed logging and error reporting

Slug Format

Slugs are formatted according to these rules:

  • Spaces replaced with hyphens (-)
  • Non-word characters removed (except hyphens and slashes)
  • Converted to lowercase

Example transformations:

  • "Goveje meso""goveje-meso"
  • "Fresh Chicken Breast""fresh-chicken-breast"
  • "Свежая курица""kurица" (Cyrillic handled appropriately)

Running the Slug Generation Job

Option 1: Via API Endpoint

Queue the job via HTTP:

curl -X POST http://localhost:3000/api/generate-product-slugs \
  -H "Authorization: Bearer YOUR_PAYLOAD_API_KEY" \
  -H "Content-Type: application/json"

This will return a job ID:

{
  "success": true,
  "jobId": "12345",
  "message": "Product slug generation job queued successfully"
}

Option 2: Via Payload Admin UI

  1. Go to the Payload admin panel
  2. Navigate to Jobs in the sidebar
  3. Find the job with task type generateProductSlugs
  4. Click Run to execute it

Checking Job Status

Use the GET endpoint with your job ID:

curl "http://localhost:3000/api/generate-product-slugs?jobId=12345" \
  -H "Authorization: Bearer YOUR_PAYLOAD_API_KEY"

Response includes:

{
  "jobId": "12345",
  "status": "completed",
  "result": {
    "success": true,
    "totalDocuments": 50,
    "totalSlugsGenerated": 87,
    "errors": []
  }
}

Implementation Details

Files Created

  1. Job Handler (src/payload/jobs/tasks/generateProductSlugs.ts)

    • Main logic for slug generation
    • Handles all locales independently
    • Includes error handling and logging
  2. API Endpoint (src/app/api/generate-product-slugs/route.ts)

    • HTTP trigger for the job
    • Status checking endpoint
    • Basic authentication
  3. Config Registration (updated payload.config.ts)

    • Registered as a Payload job task
    • Label: "Generate Product Slugs (from titles)"
    • No input parameters needed

Database Migration Required

After adding the job task to the config, you need to create and run a database migration:

# Create migration
pnpm run payload migrate:create

# Run migration
pnpm run payload migrate

This updates the PostgreSQL enum for job task slugs to include generateProductSlugs.

Examples

Example 1: New Product with Only Slovenian Title

Before:

{
  "title": {
    "sl": "Goveje meso",
    "en": "Beef Meat",
    "ru": "Говядина"
  },
  "slug": "goveje-meso" // Only Slovenian slug exists (string format)
}

After running job:

{
  "title": {
    "sl": "Goveje meso",
    "en": "Beef Meat",
    "ru": "Говядина"
  },
  "slug": {
    "sl": "goveje-meso",
    "en": "beef-meat",
    "ru": "govyadina"
  }
}

Example 2: Product with Some Missing Slugs

Before:

{
  "slug": {
    "sl": "svinje-meso",
    "en": "" // Empty English slug
    // Russian slug missing entirely
  }
}

After running job:

{
  "slug": {
    "sl": "svinje-meso", // Unchanged
    "en": "pork-meat", // Generated from English title
    "ru": "svinina" // Generated from Russian title
  }
}

Logs and Debugging

The job provides detailed logging:

=== PRODUCT SLUG GENERATION JOB STARTED ===
Found 50 product documents

=== Processing Product ID: 123 ===
Product 123 already has slug for locale sl, skipping
Generating slug for product 123 (en): "Beef Meat" -> "beef-meat"
Successfully generated slug for product 123 (en): beef-meat
Generating slug for product 123 (ru): "Говядина" -> "govyadina"
Successfully generated slug for product 123 (ru): govyadina

=== PRODUCT SLUG GENERATION COMPLETED ===
Generated 87 slugs across 50 products

Safety Features

  1. Never overwrites existing slugs: If a slug already exists for a locale, it's skipped
  2. Per-locale isolation: Each locale is processed independently
  3. Error isolation: If one product/locale fails, others continue processing
  4. Detailed error reporting: All errors are logged and returned in the result

When to Use This

Use this task when:

  • You have products with missing slugs in some locales
  • You've imported products without generating slugs
  • You've added new locale support and need to backfill slugs
  • You want to ensure all products have consistent, URL-friendly identifiers

Extending for Other Collections

This pattern can be adapted for other collections (Collections, Recipes, etc.) by:

  1. Copying the job handler and changing the collection name
  2. Adjusting the field names (if not using title and slug)
  3. Registering the new task in payload.config.ts
  4. Creating a corresponding API endpoint

Troubleshooting

Problem: Job queues but never runs

Solution: Make sure you have a job runner process active:

curl -X POST http://localhost:3000/api/payload-jobs/run \
  -H "Authorization: Bearer YOUR_CRON_SECRET"

Problem: Slugs not generating for a specific locale

Solution: Check that the product has a title in that locale. The job logs will show:

Product 123 has no title for locale en, skipping slug generation

Problem: "Duplicate slug" error

Solution: The job doesn't handle slug uniqueness conflicts. Ensure your titles are unique per locale, or manually adjust conflicting slugs before running the job.

0

Frequently Asked Questions

Comments

Leave a Comment

Your email will not be published

10-2000 characters

• Comments are automatically approved and will appear immediately

• Your name and email will be saved for future comments

• Be respectful and constructive in your feedback

• No spam, self-promotion, or off-topic content

Matija Žiberna
Matija Žiberna
Full-stack developer, co-founder

I'm Matija Žiberna, a self-taught full-stack developer and co-founder passionate about building products, writing clean code, and figuring out how to turn ideas into businesses. I write about web development with Next.js, lessons from entrepreneurship, and the journey of learning by doing. My goal is to provide value through code—whether it's through tools, content, or real-world software.