Installations
npm install mongoose-slug-plugin
Releases
Contributors
Developer
Developer Guide
Module System
CommonJS
Min. Node Version
>=8.3
Typescript Support
No
Node Version
NPM Version
Statistics
22 Stars
41 Commits
9 Forks
7 Watching
2 Branches
6 Contributors
Updated on 01 Nov 2022
Bundle Size
86.73 kB
Minified
31.04 kB
Minified + Gzipped
Languages
JavaScript (100%)
Total Downloads
Cumulative downloads
Total Downloads
114,841
Last day
17%
124
Compared to previous day
Last week
36.8%
520
Compared to previous week
Last month
66.8%
2,229
Compared to previous month
Last year
-2.6%
15,937
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
mongoose-slug-plugin
Slugs for Mongoose with history and i18n support (uses speakingurl by default, but you can use any slug library such as limax, slugify, mollusc, or slugme)
Table of Contents
- Install
- Usage
- Static Methods
- Options
- Slug Tips
- Slug Uniqueness
- Custom Slug Library
- Background
- Contributors
- License
Install
npm:
1npm install mongoose-slug-plugin
yarn:
1yarn add mongoose-slug-plugin
Usage
Add the plugin to your project (it will automatically generate a slug when the document is validated based off the template string passed)
1const mongooseSlugPlugin = require('mongoose-slug-plugin'); 2const mongoose = require('mongoose'); 3 4const BlogPost = new mongoose.Schema({ 5 title: String 6}); 7 8BlogPost.plugin(mongooseSlugPlugin, { tmpl: '<%=title%>' }); 9 10module.exports = mongoose.model('BlogPost', BlogPost);
If you need to render some custom function in the template string for display purposes, such as outputting a formatted date with dayjs:
1const dayjs = require('dayjs'); 2 3const mongooseSlugPlugin = require('mongoose-slug-plugin'); 4const mongoose = require('mongoose'); 5 6const BlogPost = new mongoose.Schema({ 7 title: { type: String, required: true, unique: true }, 8 posted_at: { type: Date, required: true } 9}); 10 11BlogPost.plugin(mongooseSlugPlugin, { 12 tmpl: "<%=title%>-<%=dayjs(posted_at).format('YYYY-MM-DD')%>", 13 locals: { dayjs } 14}); 15 16module.exports = mongoose.model('BlogPost', BlogPost);
If you're using Koa, here's an example showing how to lookup a slug or an archived slug and properly 301 redirect:
1const Koa = require('koa'); 2const Router = require('koa-router'); 3const Boom = require('boom'); 4 5const BlogPosts = require('./blog-post'); 6 7const app = new Koa(); 8const router = new Router(); 9 10router.get('/blog/:slug', async (ctx, next) => { 11 try { 12 // lookup the blog post by the slug parameter 13 const blogPost = await BlogPosts.findOne({ slug: ctx.params.slug }); 14 15 // if we found it then return early and render the blog post 16 if (blogPost) return ctx.render('blog-post', { title: blogPost.title, blogPost }); 17 18 // check if the slug changed for the post we're trying to lookup 19 blogPost = await BlogPosts.findOne({ slug_history: ctx.params.slug }); 20 21 // 301 permanent redirect to new blog post slug if it was found 22 if (blogPost) return ctx.redirect(301, `/blog/${blogPost.slug}`); 23 24 // if no blog post found then throw a nice 404 error 25 // this assumes that you're using `koa-better-error-handler` 26 // and also using `koa-404-handler`, but you don't necessarily need to 27 // since koa automatically sets 404 status code if nothing found 28 // <https://github.com/ladjs/koa-better-error-handler> 29 // <https://github.com/ladjs/koa-404-handler> 30 return next(); 31 32 } catch (err) { 33 ctx.throw(err); 34 } 35}); 36 37app.use(router.routes()); 38app.listen(3000);
If you're using Express, here's an example showing how to lookup a slug or an archived slug and properly 301 redirect:
1TODO
Note that you also have access to a static function on the model called
getUniqueSlug
.
This function accepts an _id
and str
argument. The _id
being the ObjectID of the document and str
being the slug you're searching for to ensure uniqueness.
This function is used internally by the plugin to recursively ensure uniqueness.
Static Methods
If you have to write a script to automatically set slugs across a collection, you can use the getUniqueSlug
static method this package exposes on models.
For example, if you want to programmatically set all blog posts to have slugs, run this script (note that you should run the updates serially as the example shows to prevent slug conflicts):
1const Promise = require('bluebird'); // exposes `Promise.each` 2 3const BlogPost = require('../app/models/blog-post.js'); 4 5(async () => { 6 const blogPosts = await BlogPost.find({}).exec(); 7 await Promise.each(blogPosts, async blogPost => { 8 blogPost.slug = null; 9 blogPost.slug = await BlogPost.getUniqueSlug(blogPost._id, blogPost.title); 10 return blogPost.save(); 11 })); 12})();
Options
Here are the default options passed to the plugin:
tmpl
(String) - Required, this should be a lodash template string (e.g.<%=title%>
to use the blog post title as the slug)locals
(Object) - Defaults to an empty object, but you can pass a custom object that will be inherited for use in the lodash template string (see above example for how you could use dayjs to render a document's date formatted in the slug)alwaysUpdateSlug
(Boolean) - Defaults totrue
(basically this will re-set the slug to the value it should be based off the template string every time the document is validated (or saved for instance due to pre-save hook in turn calling pre-validate in Mongoose)errorMessage
(String) - Defaults toSlug was missing or blank
, this is a String that is returned for failed validation (note that it gets translated based off thethis.locale
field if it is set on the document (see Lad for more insight into how this works))logger
(Object) - defaults toconsole
, but you might want to use Lad's loggerslugField
(String) - defaults toslug
, this is the field used for storing the slug for the documenthistoryField
(String) - defaults toslug_history
, this is the field used for storing a document's slug historyi18n
(Object|Boolean) - defaults tofalse
, but accepts ai18n
object from Lad's i18nslug
(Function) - Defaults tospeakingurl
, but it is a function that converts a string into a slug (see below Custom Slug Libary examples)slugOptions
(Object) - An object of options to pass to the slug function when invoked as specified inoptions.slug
Slug Tips
If you're using the default slug library speakingurl
, then you might want to pass the option slugOptions: { "'": '' }
in order to fix contractions.
For example, if your title is "Jason's Blog Post", you probably want the slug to be "jasons-blog-post" as opposed to "jason-s-blog-post". This option will fix that.
See pid/speakingurl#105 for more information.
Slug Uniqueness
If a slug of "foo-bar" already exists, and if we are inserting a new document that also has a slug of "foo-bar", then this new slug will automatically become "foo-bar-1".
Custom Slug Library
If you don't want to use the library speakingurl
for generating slugs (which this package uses by default), then you can pass a custom slug
function:
limax example:
1const limax = require('limax'); 2 3BlogPost.plugin(mongooseSlugPlugin, { tmpl: '<%=title%>', slug: limax });
slugify example:
1const slugify = require('slugify'); 2 3BlogPost.plugin(mongooseSlugPlugin, { tmpl: '<%=title%>', slug: slugify });
mollusc example:
1const slug = require('mollusc'); 2 3BlogPost.plugin(mongooseSlugPlugin, { tmpl: '<%=title%>', slug });
slugme example:
1const slugme = require('slugme'); 2 3BlogPost.plugin(mongooseSlugPlugin, { tmpl: '<%=title%>', slug: slugme });
Background
I created this package despite knowing that other alternatives like it exist for these reasons:
- No alternative supported i18n localization/translation out of the box
- No alternative used the well-tested and SEO-friendly
speakingurl
package - No alternative allowed users to pass their own slug library
- No alternative documented how to clearly do a 301 permanent redirect for archived slugs
- No alternative allowed the field names to be customized
- No alternative had decent tests written
Contributors
Name | Website |
---|---|
Nick Baugh | http://niftylettuce.com/ |
shadowgate15 | https://github.com/shadowgate15 |
License
MIT © Nick Baugh
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
security policy file detected
Details
- Info: security policy file detected: github.com/ladjs/.github/SECURITY.md:1
- Info: Found linked content: github.com/ladjs/.github/SECURITY.md:1
- Info: Found disclosure, vulnerability, and/or timelines in security policy: github.com/ladjs/.github/SECURITY.md:1
- Info: Found text in security policy: github.com/ladjs/.github/SECURITY.md:1
Reason
Found 3/27 approved changesets -- score normalized to 1
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 6 are checked with a SAST tool
Reason
40 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw
- Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw
- Warn: Project is vulnerable to: GHSA-pp7h-53gx-mx7r
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-xp63-6vf5-xf3v
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c
- Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq
- Warn: Project is vulnerable to: GHSA-ff7x-qrg7-qggm
- Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6
- Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97
- Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj
- Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j
- Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37
- Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
- Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw
- Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9
- Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3
- Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h
- Warn: Project is vulnerable to: GHSA-f825-f98c-gj3g
- Warn: Project is vulnerable to: GHSA-h8hf-x3f4-xwgp
- Warn: Project is vulnerable to: GHSA-9m93-w8w6-76hh
- Warn: Project is vulnerable to: GHSA-p92x-r36w-9395
- Warn: Project is vulnerable to: GHSA-45q2-34rf-mr94
- Warn: Project is vulnerable to: GHSA-w7rc-rwvf-8q5r
- Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g
- Warn: Project is vulnerable to: GHSA-px4h-xg32-q955
- Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-44c6-4v22-4mhx
- Warn: Project is vulnerable to: GHSA-4x5v-gmq8-25ch
- Warn: Project is vulnerable to: GHSA-w5p7-h5w8-2hfq
- Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v
- Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7
- Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7
- Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh
- Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp
Score
2.7
/10
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More