directus-extension-permalinks
v0.3.0
Published
Permalink management extension for Directus — unique cross-collection URL slugs with an interface, hook, endpoint, and module
Maintainers
Readme
directus-extension-permalinks
Permalink management for Directus 11. Maintain a unique URL registry across every collection in your CMS — with real-time conflict detection, URL restrictions, and automatic redirects when slugs change.
Features
- Cross-collection uniqueness — no two items can share the same permalink, regardless of collection.
permalinkinterface — replaces the plain text input. Adds auto-generation from a sibling field (e.g.title), real-time availability checking, and a frontend preview link.permalink-linkinterface — a link picker for fields that reference internal pages or external URLs. Autocomplete from the registry, with FK storage so links stay fresh when the target URL changes.- FK-based storage — both interfaces store the UUID of the
ext_permalinksrow, not the plain text. Changing a page's URL updates one row and all references resolve to the new value automatically. /permalinks/resolveendpoint — lets your frontend resolve any URL path to a collection + item without hard-coded routing logic.- Automatic 301 redirects — when a permalink changes, a redirect is created automatically from the old path to the new one.
- Loop and chain protection — the redirect engine prevents loops (A→B + B→A) and collapses chains (A→B→C becomes A→C).
- URL restrictions — block specific paths, prefixes, or regex patterns from being used as permalinks (e.g.
/admin,/api). - Admin module — a dedicated Directus UI for the full permalink registry, settings, and redirect management.
Installation
npm install directus-extension-permalinksThen restart Directus. The extension creates its database tables and registers itself as Directus collections automatically on startup.
Manual installation
If you prefer to install without npm, copy the dist/ folder into your Directus extensions directory and restart:
extensions/directus-extension-permalinks/dist/todo
TODO : Marketplace installation notes
Setup
1. Add a permalink field to a collection
In the Directus admin, add a new field to any collection (e.g. pages):
- Type: UUID
- Interface: Permalink
Interface options:
| Option | Description |
|---|---|
| Generate From | Sibling field(s) to auto-generate the slug from. Use {{ field }} syntax (e.g. {{ title }}). |
| Prefix | Display-only path prefix shown in the preview URL (e.g. /blog). Not stored. |
2. Configure the frontend URL
In the Directus admin, navigate to Permalinks → Settings and enter your frontend base URL (e.g. https://mysite.com). This is used to render preview links in the permalink interface.
3. Use the resolve endpoint in your frontend
Instead of querying a collection directly by slug, call:
GET /permalinks/resolve?path=/your-slugResponse (200 — found):
{
"type": "record",
"collection": "pages",
"field": "permalink",
"item_id": "abc-123",
"permalink": "/your-slug"
}Response (200 — redirect):
{
"type": "redirect",
"destination": "/new-slug",
"statusCode": 301
}The endpoint always returns JSON. Your frontend decides whether to issue an HTTP redirect or fetch the new URL directly.
How permalink-link works
When an editor picks an internal page via the link interface, the field stores the UUID of the ext_permalinks row — not the text. This means:
- If the target page's URL later changes (e.g.
/about→/about-us), every item that linked to it will automatically resolve to/about-us— no content updates needed. - External URLs (
https://…) are also stored asext_permalinksrows, deduplicated by URL.
REST API Reference
Public (no auth required)
| Method | Path | Description |
|---|---|---|
| GET | /permalinks/resolve?path= | Resolve a URL path to collection + item |
| GET | /permalinks/check?value=&collection=&field= | Check if a permalink is available |
| GET | /permalinks/search?q=&limit= | Search the registry (used by permalink-link autocomplete) |
Authenticated (any logged-in user)
| Method | Path | Description |
|---|---|---|
| GET | /permalinks/:id | Fetch a single permalink record by UUID |
| POST | /permalinks/pending | Create a pending permalink row for a new item |
Admin only
| Method | Path | Description |
|---|---|---|
| GET | /permalinks | List all permalinks (paginated, ?type=internal\|external) |
| PATCH | /permalinks/:id | Update permalink text (creates redirect automatically) |
| DELETE | /permalinks/:id | Delete permalink and nullify referencing link fields |
| GET/POST/DELETE | /permalinks/restrictions[/:id] | Manage URL restrictions |
| GET/PATCH | /permalinks/settings | Read / update settings |
| GET/POST/DELETE | /permalinks/redirects[/:id] | Manage redirects |
URL Restrictions
Restrictions block certain paths from being registered as permalinks.
| Type | Example pattern | Matches |
|---|---|---|
| exact | /sitemap.xml | Exactly /sitemap.xml |
| prefix | /admin | /admin and /admin/* |
| regex | ^/api/ | Any path starting with /api/ |
Manage restrictions in Permalinks → Settings → URL Restrictions.
Redirects
Redirects are stored in ext_permalink_redirects and served by GET /permalinks/resolve.
- Automatic — created whenever a
permalinkfield value is changed via the interface. - Manual — add, edit, and delete via Permalinks → Settings → Redirects.
The redirect engine (upsertRedirect) handles edge cases:
- Loop prevention: A→B + B→A → only B→A is kept.
- Chain collapsing: A→B + B→C → A→C (single hop).
Development
npm run dev # watch mode (rebuilds on change)
npm test # run all tests
npm run build # production buildSee docs/architecture.md for the full internal design.
License
MIT
