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

📚 Comprehensive Payload CMS Guides
Detailed Payload guides with field configuration examples, custom components, and workflow optimization tips to speed up your CMS development process.
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, andrubased on their respective titles - Safe operation: Never overwrites existing slugs
How It Works
The slug generation process:
- Fetches all products from the database with all locales
- For each product, checks each locale (
sl,en,ru) - 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
- 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
- Go to the Payload admin panel
- Navigate to Jobs in the sidebar
- Find the job with task type
generateProductSlugs - 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
-
Job Handler (
src/payload/jobs/tasks/generateProductSlugs.ts)- Main logic for slug generation
- Handles all locales independently
- Includes error handling and logging
-
API Endpoint (
src/app/api/generate-product-slugs/route.ts)- HTTP trigger for the job
- Status checking endpoint
- Basic authentication
-
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
- Never overwrites existing slugs: If a slug already exists for a locale, it's skipped
- Per-locale isolation: Each locale is processed independently
- Error isolation: If one product/locale fails, others continue processing
- 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:
- Copying the job handler and changing the collection name
- Adjusting the field names (if not using
titleandslug) - Registering the new task in
payload.config.ts - 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.
Related
- Automating Payload Translations with OpenAI - Similar pattern for translating content
src/utilities/formatSlug.ts- Core slug formatting logicsrc/fields/slug.ts- Slug field definition and hooks