Plz yurn on JavaScript

What I found by building my own NodeJS boilerplate.

May 03, 20173 minutes to read

  • #nodejs
  • #javascript
  • #mongodb
  • #express
  • #tips
  • #npm packages

Mongoose Tips

I’ve played with Mongoose a lot in the past week. I’ve built a NodeJS API boilerplate for help me kickstart some REST API project. I setup the regular auth using PassportJS with the local and JWT strategies. By doing this I found some useful tricks with Mongoose. Some tips I never really see somewhere ‘maybe I didn’t search lot 😃’ and I want to share you what I found.

toJSON()

Example, you want to make authentication with your app and you don’t want to send the password to the front-end. It’s normal cause this is a big security issue if you did. So want you can do it’s create a function who take your user and return a new object.

1function getUser(user) {
2 return {
3 _id: user._id,
4 username: user.username,
5 };
6}

This strategy work but I think the one I’m gonna show gonna be better.

1UserSchema.methods = {
2 /**
3 * Authenticate the user
4 *
5 * @public
6 * @param {String} password - provided by the user
7 * @returns {Boolean} isMatch - password match
8 */
9 authenticateUser(password) {
10 return compareSync(password, this.password);
11 },
12 /**
13 * Hash the user password
14 *
15 * @private
16 * @param {String} password - user password choose
17 * @returns {String} password - hash password
18 */
19 _hashPassword(password) {
20 return hashSync(password);
21 },
22
23 /**
24 * Generate a jwt token for authentication
25 *
26 * @public
27 * @returns {String} token - JWT token
28 */
29 createToken() {
30 return jwt.sign(
31 {
32 _id: this._id,
33 },
34 constants.JWT_SECRET,
35 );
36 },
37
38 /**
39 * Parse the user object in data we wanted to send when is auth
40 *
41 * @public
42 * @returns {Object} User - ready for auth
43 */
44 toAuthJSON() {
45 return {
46 _id: this._id,
47 token: `JWT ${this.createToken()}`,
48 };
49 },
50
51 /**
52 * Parse the user object in data we wanted to send
53 *
54 * @public
55 * @returns {Object} User - ready for populate
56 */
57 toJSON() {
58 return {
59 _id: this._id,
60 username: this.username,
61 };
62 },
63};

So here we have a lot of stuff to check 😃. Methods in mongoose are finally what they said. They are methods available on your user object. An example here we have js±authenticateUser(password) who is use for authenticate the user find with email if the password is the right one. Same go for the js±_hashPassword(password) who just simply hash the password before saving the user in the DB. js±createToken() like the name say create the JWT token and can be user right inside the response

1res.status(200).json({ user, token: user.createToken() })

But the one I want you to see it's the js±toJSON(). This on is use when finally you on your user. So if you check back

1res.status(200).json({ user, token: user.createToken() })

you can see I send the user. Because we have the js±toJSON() on make it working just like this. We don't send timestamps, email, password etc. We just send js±_id and js±username nothing more. But ok why do the js±toAuthJSON()? Because now I can reformat the response to be

1res.status(200).send(user.toAuthJSON())

so I just send an Object with _id and token. Hope this part make sense 😃.

The reason why have to methods for JSON below 😃.

Statics

After that in Mongoose, you have access to something call Statics in your schema. This is the same thing like in class. Statics are method who can be used without initiate this one. So you can use it right with the model himself.

Example

1PostSchema.statics = {
2 /**
3 * Create a post
4 *
5 * @public
6 * @param {Object} args - Object contains title and text
7 * @param {String} authorId - the author id
8 * @returns {Post} Post Object - new post create
9 */
10 createPost(args, authorId) {
11 return this.create({
12 ...args,
13 author: authorId,
14 });
15 },
16
17 list({ skip = 0, limit = 10 }) {
18 return this.find()
19 .sort({ createdAt: -1 })
20 .skip(skip)
21 .limit(limit)
22 .populate('author');
23 },
24};

Here I have 2 statics methods to my post. This method finally is just for abstract some of your code. For me, that make my life a bit easier and make the controller cleaner. The js±createPost(args, authorId) it's for just clean up a bit the code. I can use it by doing

1Post.createPost({ title: 'Hello' }, '123')

I just remove some code and make it a bit easier when it came to maybe change DB. I can keep the same controller but just change my js±Post services.

After this one we have

1list({ skip = 0, limit = 10 })

This one it's just for make kind of pagination easier. You can see I use the ES6 feature Default Parameters who let me add default parameters if these values are js±undefined. Again I can use it like that

1Post.list({ skip: 5, limit: 20 });

This is again for me just sugars and makes my code easier to follow.

Again toJSON() 😃

In the last example in the list we have js±.populate('author');. Because of the js±toJSON() by default the user gonna have only js±_id and js±username no need to add select value etc :). That's why I have js±toAuthJSON() who is called on login and js±toJSON() for this kind of thing.

Packages

Some packages I didn't know in the NodeJS ecosystem and need to be used 😄.

Joi

I really like this one for help me make validation in my controller. So easy to use too.

1export const validation = {
2 create: {
3 body: {
4 title: Joi.string().min(3).required(),
5 text: Joi.string().required(),
6 },
7 },
8 update: {
9 body: {
10 title: Joi.string().min(3),
11 text: Joi.string(),
12 },
13 },
14};

After this, in your routes file, you do with the help of express-validation

1routes.post(
2 '/',
3 authJwt,
4 validate(PostController.validation.create),
5 PostController.create,
6);
7routes.patch(
8 '/:id',
9 authJwt,
10 validate(PostController.validation.update),
11 PostController.updatePost,
12);

Helmet

Helmet's a library who help you secure your Express app. Easy to install just need to add it as a middleware js±app.use(helmet()). This is for getting the standard. You can check on their GitHub to see another way.

Cors

Cors's a middleware who enable for you the Cross-Origin request. Can be added for getting everything working just by doing js±app.use(cors()) but it's a good thing to whitelist your front-end only etc. Take again a look at the docs before use it.

Http-Status

Http-Status just make your life easier to add status to your endpoint.

1export async function getList(req, res, next) {
2 try {
3 return res
4 .status(HTTPStatus.OK)
5 .json(await Post.list({ skip: req.query.skip, limit: req.query.limit }));
6 } catch (err) {
7 err.status = HTTPStatus.BAD_REQUEST;
8 return next(err);
9 }
10}

Other useful packages

Prettier

Prettier help you to reformat your code and make it look better in no time. I start to use it about 1 month ago and now use it on every project I do. Easy to install this packages gonna save you time and gonna make your code look much better. PS if you use it with eslint and have a lot of red error maybe add eslint-config-prettier to your project and add it to your extends in .eslintrc. This gonna remove eslint issue with syntax looking and prettier gonna manage it.

Example

1{
2 "extends": [
3 "equimper",
4 "prettier"
5 ]
6}

Lint-Staged

Lint-Staged gonna run your linter on your commit. Why have it ? Because maybe you use eslint and prettier and forgot all time to run the scripts. So your code looks bad etc. By adding this tools your commit gonna be linting before that let your commit. Can be really useful for a project with a lot of people.

For adding it I just add this in my packages.json

1{
2 "pre-commit": "lint-staged",
3 "lint-staged": {
4 "*.js": [
5 "eslint",
6 "yarn prettier",
7 "git add"
8 ]
9 },
10 "scripts": {
11 "lint": "eslint src --color",
12 "prettier": "node ./scripts/prettier.js write",
13 "lint-staged": "lint-staged",
14 }
15}

End word

Hope this little article was a little gold mine of packages and tips for you. That was a really good experience working on this simple boilerplate. Plz take a look at it and let me know what you think of it.

NodeJS-API-Boilerplate

Previous

Make iterm more powerful

Next

Why I moved away from Atom to Visual Studio Code and my Setup

Join my Newsletter

Subscribe to get my latest content & deal for my incoming courses.

    I respect your privacy. Unsubscribe at any time.

    Powered By ConvertKit

    Comments