{"id":13781513,"url":"https://github.com/adonisjs/lucid-slugify","last_synced_at":"2025-04-12T18:49:19.001Z","repository":{"id":49119486,"uuid":"122715924","full_name":"adonisjs/lucid-slugify","owner":"adonisjs","description":"Lucid slugify gives you a simple API to create and persist unique slugs to the database","archived":false,"fork":false,"pushed_at":"2025-03-09T12:13:26.000Z","size":1165,"stargazers_count":114,"open_issues_count":0,"forks_count":8,"subscribers_count":7,"default_branch":"3.x","last_synced_at":"2025-04-05T17:01:40.540Z","etag":null,"topics":["first-party-package","lucid"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adonisjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"thetutlage"}},"created_at":"2018-02-24T07:35:11.000Z","updated_at":"2025-04-04T03:46:03.000Z","dependencies_parsed_at":"2024-11-11T01:21:41.871Z","dependency_job_id":"d643c359-9f15-47b3-b369-68bdbce0df38","html_url":"https://github.com/adonisjs/lucid-slugify","commit_stats":{"total_commits":58,"total_committers":4,"mean_commits":14.5,"dds":0.08620689655172409,"last_synced_commit":"27451b3990453df6f9b23104774423f90321e8a1"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Flucid-slugify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Flucid-slugify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Flucid-slugify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Flucid-slugify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adonisjs","download_url":"https://codeload.github.com/adonisjs/lucid-slugify/tar.gz/refs/heads/3.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248618218,"owners_count":21134199,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["first-party-package","lucid"],"created_at":"2024-08-03T18:01:26.671Z","updated_at":"2025-04-12T18:49:18.973Z","avatar_url":"https://github.com/adonisjs.png","language":"TypeScript","funding_links":["https://github.com/sponsors/thetutlage"],"categories":["Packages"],"sub_categories":[],"readme":"# @adonisjs/lucid-slugify\n\n\u003e Use Lucid models to create URL-safe unique slugs and persist them in the database.\n\n\u003cbr /\u003e\n\n[![gh-workflow-image]][gh-workflow-url] [![npm-image]][npm-url] ![][typescript-image] [![license-image]][license-url]\n\n## Introduction\n\nGenerating slugs is easy, but keeping them unique is hard. This package abstracts the hard parts and gives you a simple API to create and persist unique slugs to the database.\n\nLucid slugify exports the `@slugify` decorator, which you can use on the model fields to mark them as slugs and define the source fields from which the slug should be generated. Under the hood, the decorator registers `beforeCreate` and `beforeUpdate` hooks to compute the slug and persist it to the database.\n\nIn the following example, we mark the `slug` field as the slug and compute its value using the `title` field. We also use the [dbIncrement](#dbincrement) strategy to keep slugs unique.\n\n```ts\nimport { DateTime } from 'luxon'\nimport { BaseModel, column } from '@adonisjs/lucid/orm'\nimport { slugify } from '@adonisjs/lucid-slugify'\n\nexport default class Post extends BaseModel {\n  @column({ isPrimary: true })\n  declare id: number\n\n  @column()\n  declare title: string\n\n  @column()\n  @slugify({\n    strategy: 'dbIncrement',\n    fields: ['title'],\n  })\n  declare slug: string\n}\n```\n\n## Installation and usage\n\nYou can install the `@adonisjs/lucid-slugify` package from the npm packages registry. Ensure your application uses `@adonisjs/core@6` and `@adonisjs/lucid@21`.\n\n```sh\nnpm i @adonisjs/lucid-slugify\n```\n\n```sh\nyarn add @adonisjs/lucid-slugify\n```\n\n```sh\npnpm add @adonisjs/lucid-slugify\n```\n\nOnce done, you can mark a field as a slug using the `@slugify` decorator. Make sure to specify the source field(s) from which the slug should be generated.\n\n```ts\nimport { DateTime } from 'luxon'\nimport { BaseModel, column } from '@adonisjs/lucid/orm'\n\n// 👇 Import decorator\nimport { slugify } from '@adonisjs/lucid-slugify'\n\nexport default class Post extends BaseModel {\n  @column({ isPrimary: true })\n  declare id: number\n\n  @column()\n  declare title: string\n\n  @column()\n  // 👇 Use it on a column\n  @slugify({\n    fields: ['title'],\n  })\n  declare slug: string\n}\n```\n\n## Uniqueness of slug\n\nIn the previous example, if two posts are created with the same `title`, they will have the same `slug` value.\n\nThis won't be a problem if you are the only author of your blog since you can always rename titles or might never write two articles with the same title.\n\nHowever, if it's a community blog or forum, the chances of creating two or more posts with the same title are quite high.\n\nYou can use one of the following strategies to prevent duplicate slugs even when the titles are the same.\n\n### dbIncrement\n\nThe `dbIncrement` strategy performs a select query to find similar slugs and appends a counter when a duplicate slug is found. Given you have a database table with the following slugs:\n\n- Creating a post with `slug=hello-world` will result in `hello-world-6`.\n- Similarly, creating a post with `slug=introduction-to-social-auth` will result in `introduction-to-social-auth-5`.\n\n```\n+----+-----------------------------+-------------------------------+\n| id | title                       | slug                          |\n+----+-----------------------------+-------------------------------+\n| 1  | Hello world                 | hello-world                   |\n| 2  | Hello world                 | hello-world-5                 |\n| 3  | Hello world                 | hello10world                  |\n| 4  | Hello world                 | hello-10-world                |\n| 5  | Introduction to social auth | introduction-to-social-auth   |\n| 6  | Introduction to social auth | introduction-to-social-auth-4 |\n| 7  | Hello world                 | hello-world-2                 |\n| 8  | Hello world fanny           | hello-world-fanny             |\n| 9  | Hello world                 | post-hello-world              |\n| 10 | Hello world                 | post-11am-hello-world11       |\n| 11 | Hello world                 | post-11am-hello-world         |\n| 12 | Introduction to social auth | introdUction-to-Social-auTH-1 |\n+----+-----------------------------+-------------------------------+\n```\n\n### shortId\n\nThe `shortId` strategy appends a **10-digit short id** to the slug to make it unique. This strategy does not perform any additional database queries.\n\n```ts\nexport default class Post extends BaseModel {\n  @column({ isPrimary: true })\n  declare id: number\n\n  @column()\n  declare title: string\n\n  @column()\n  @slugify({\n    strategy: 'shortId',\n    fields: ['title'],\n  })\n  declare slug: string\n}\n```\n\n```\n+----+-------------+------------------------+\n| id | title       | slug                   |\n+----+-------------+------------------------+\n| 1  | Hello world | hello-world-yRPZZIWGgC |\n+----+-------------+------------------------+\n```\n\n## Updating slugs\n\nBy default, slugs are not updated when you update a model instance, and this is how it should be when slugs are used to look up a record, as changing a slug will result in a broken URL.\n\nHowever, if slugs are not primarily used to look up records, you may want to update them.\n\nYou can enable updates by using the `allowUpdates` flag.\n\n```ts\nexport default class Post extends BaseModel {\n  @column({ isPrimary: true })\n  declare id: number\n\n  @column()\n  declare title: string\n\n  @column()\n  @slugify({\n    strategy: 'dbIncrement',\n    fields: ['title'],\n    allowUpdates: true, // 👈\n  })\n  declare slug: string\n}\n```\n\n## Null values and slug generation\n\nThe `slugify` decorator does not generate slugs when the value of one or more source fields is `undefined` or `null`.\n\n## Available options\n\n## Available options\n\nFollowing is the list of available options accepted by the `@slugify` decorator.\n\n\u003ctable\u003e\n\u003ctr\u003e\n  \u003ctd colspan=\"2\"\u003e\u003ccode\u003e{\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd valign=\"top\"\u003e\u003ccode\u003e\"fields\":\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\n    \u003cp\u003e\n    An array of source fields to use for generating the slug. The value of multiple fields is concatenated using the \u003ccode\u003econfig.separator\u003c/code\u003e property.\n    \u003c/p\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd valign=\"top\"\u003e\u003ccode\u003e\"strategy\":\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\n    \u003cp\u003e\n    Reference to a pre-existing strategy or a factory function that returns a custom strategy implementation.\n    \u003c/p\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd valign=\"top\"\u003e\u003ccode\u003e\"allowUpdates\":\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\n    \u003cp\u003e\n    A boolean to enable updates. \u003cstrong\u003eUpdates are disabled by default\u003c/strong\u003e.\n    \u003c/p\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd valign=\"top\"\u003e\u003ccode\u003e\"maxLength\":\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\n    \u003cp\u003e\n    The maximum length for the generated slug. The final slug value can be slightly over the defined \u003ccode\u003emaxLength\u003c/code\u003e in the following scenarios.\n    \u003c/p\u003e\n    \u003cp\u003e\n      \u003cstrong\u003eNo max length is applied by default.\u003c/strong\u003e\n    \u003c/p\u003e\n  \u003cul\u003e\n  \u003cli\u003e\n    When \u003ccode\u003ecompleteWords\u003c/code\u003e is set to true.\n  \u003c/li\u003e\n  \u003cli\u003e\n    When using the \u003ccode\u003edbIncrement\u003c/code\u003e strategy. The counter value is appended after trimming the value for the \u003ccode\u003emaxLength\u003c/code\u003e.\n  \u003c/li\u003e\n  \u003c/ul\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd valign=\"top\"\u003e\u003ccode\u003e\"completeWords\":\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\n    \u003cp\u003e\n    A boolean that forces to complete the words when applying the \u003ccode\u003emaxLength\u003c/code\u003e property. Completing words will generate a slug larger than the \u003ccode\u003emaxLength\u003c/code\u003e. So, keep some buffer between the maxLength property and the database storage size.\n    \u003c/p\u003e\n    \u003cp\u003e\n      \u003cstrong\u003eComplete words are disabled by default.\u003c/strong\u003e\n    \u003c/p\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd valign=\"top\"\u003e\u003ccode\u003e\"separator\":\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\n    \u003cp\u003e\n    The separator to use for creating the slug. \u003cstrong\u003eA dash \u003ccode\u003e-\u003c/code\u003e is used by default.\u003c/strong\u003e\n    \u003c/p\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd valign=\"top\"\u003e\u003ccode\u003e\"transformer\":\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\n    \u003cp\u003e\n    A custom function to convert non-string data types to a string value. For example, if the source field from which the slug is generated is a boolean, then we will convert it to \u003ccode\u003e\"1\"\u003c/code\u003e or \u003ccode\u003e\"0\"\u003c/code\u003e.\n    \u003c/p\u003e\n    \u003cp\u003e\n    By defining the \u003ccode\u003etransformer\u003c/code\u003e property, you can decide how different data types can be converted to a string.\n    \u003c/p\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=\"2\"\u003e\u003ccode\u003e}\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Using custom strategies\n\nCustom strategies can be used if you want to handle the uniqueness of slugs yourself. A strategy must implement the [SlugifyStrategyContract](https://github.com/adonisjs/lucid-slugify/blob/3.x/src/types.ts#L130).\n\n```ts\nimport { SlugifyStrategyContract } from '@adonisjs/lucid-slugify/types'\n\nexport class MyCustomStrategy implements SlugifyStrategyContract {\n  maxLengthBuffer: number = 0\n\n  async makeSlugUnique(modelInstance: LucidRow, field: string, value: string): string {}\n}\n```\n\nThe `makeSlugUnique` method receives the following arguments.\n\n- `modelInstance`: Reference to the model instance that will be persisted in the database\n- `field`: The name of the field for which the unique slug will be created.\n- `value`: The base value to convert to a unique value.\n\nOnce you have created the strategy, you can use it with the `@slugify` decorator, as shown in the following example.\n\n```ts\nexport default class Post extends BaseModel {\n  @column({ isPrimary: true })\n  declare id: number\n\n  @column()\n  declare title: string\n\n  @column()\n  @slugify({\n    strategy: () =\u003e {\n      return new MyCustomStrategy()\n    },\n    fields: ['title'],\n  })\n  declare slug: string\n}\n```\n\n## Self creating slugs\n\nThe default implementation used by Lucid slugify for creating slugs works great with English words. However, if you are using Non-Latin alphabets, replace the implementation for creating slugs with a custom one.\n\nYou can override the static `slugify` method on the `Slugifier` class. The following code has to be executed only once.\n\n```ts\nimport { Slugifier } from '@adonisjs/lucid-slugify'\n\n/**\n * Make sure to install the \"transliteration\" package\n */\nimport { slugify } from 'transliteration'\n\nSlugifier.slugify = function (value, options) {\n  return slugify(value, {\n    separator: options.separator,\n    lowercase: options.lower,\n  })\n}\n```\n\n## Contributing\n\nOne of the primary goals of AdonisJS is to have a vibrant community of users and contributors who believe in the principles of the framework.\n\nWe encourage you to read the [contribution guide](https://github.com/adonisjs/.github/blob/main/docs/CONTRIBUTING.md) before contributing to the framework.\n\n## Code of Conduct\n\nTo ensure that the AdonisJS community is welcoming to all, please review and abide by the [Code of Conduct](https://github.com/adonisjs/.github/blob/main/docs/CODE_OF_CONDUCT.md).\n\n## License\n\nAdonisJS Lucid slugify is open-sourced software licensed under the [MIT license](LICENSE.md).\n\n[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/adonisjs/lucid-slugify/checks.yml?style=for-the-badge\n[gh-workflow-url]: https://github.com/adonisjs/lucid-slugify/actions/workflows/checks.yml 'Github action'\n[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge\u0026logo=typescript\n[typescript-url]: \"typescript\"\n[npm-image]: https://img.shields.io/npm/v/@adonisjs/lucid-slugify.svg?style=for-the-badge\u0026logo=npm\n[npm-url]: https://npmjs.org/package/@adonisjs/lucid-slugify 'npm'\n[license-image]: https://img.shields.io/npm/l/@adonisjs/lucid-slugify?color=blueviolet\u0026style=for-the-badge\n[license-url]: LICENSE.md 'license'\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadonisjs%2Flucid-slugify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadonisjs%2Flucid-slugify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadonisjs%2Flucid-slugify/lists"}