Skip to content

Commit

Permalink
Handle mongoose to json conversions in a custom plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
hagopj13 committed May 16, 2020
1 parent 86a5e07 commit f8ba3f6
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/controllers/auth.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const register = catchAsync(async (req, res) => {
}
const user = await User.create(req.body);
const tokens = await tokenService.generateAuthTokens(user.id);
const response = { user: user.transform(), tokens };
const response = { user, tokens };
res.status(httpStatus.CREATED).send(response);
});

Expand All @@ -21,7 +21,7 @@ const login = catchAsync(async (req, res) => {
throw new ApiError(httpStatus.UNAUTHORIZED, 'Incorrect email or password');
}
const tokens = await tokenService.generateAuthTokens(user.id);
const response = { user: user.transform(), tokens };
const response = { user, tokens };
res.send(response);
});

Expand Down
8 changes: 4 additions & 4 deletions src/controllers/user.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ const createUser = catchAsync(async (req, res) => {
throw new ApiError(httpStatus.BAD_REQUEST, 'Email already taken');
}
const user = await User.create(req.body);
res.status(httpStatus.CREATED).send(user.transform());
res.status(httpStatus.CREATED).send(user);
});

const getUsers = catchAsync(async (req, res) => {
const filter = pick(req.query, ['name', 'role']);
const options = getQueryOptions(req.query);
const users = await User.find(filter, null, options);
const response = users.map((user) => user.transform());
const response = users;
res.send(response);
});

Expand All @@ -26,7 +26,7 @@ const getUser = catchAsync(async (req, res) => {
if (!user) {
throw new ApiError(httpStatus.NOT_FOUND, 'User not found');
}
res.send(user.transform());
res.send(user);
});

const updateUser = catchAsync(async (req, res) => {
Expand All @@ -39,7 +39,7 @@ const updateUser = catchAsync(async (req, res) => {
}
Object.assign(user, req.body);
await user.save();
res.send(user.transform());
res.send(user);
});

const deleteUser = catchAsync(async (req, res) => {
Expand Down
6 changes: 4 additions & 2 deletions src/models/token.model.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const mongoose = require('mongoose');
const { toJSON } = require('./utils');

const tokenSchema = mongoose.Schema(
{
Expand Down Expand Up @@ -28,11 +29,12 @@ const tokenSchema = mongoose.Schema(
},
{
timestamps: true,
toObject: { getters: true },
toJSON: { getters: true },
}
);

// add plugin that converts mongoose to json
tokenSchema.plugin(toJSON);

const Token = mongoose.model('Token', tokenSchema);

module.exports = Token;
18 changes: 5 additions & 13 deletions src/models/user.model.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const { omit, pick } = require('lodash');
const { toJSON } = require('./utils');
const { roles } = require('../config/roles');

const userSchema = mongoose.Schema(
Expand Down Expand Up @@ -33,6 +33,7 @@ const userSchema = mongoose.Schema(
throw new Error('Password must contain at least one letter and one number');
}
},
private: true, // used by the toJSON plugin
},
role: {
type: String,
Expand All @@ -42,11 +43,12 @@ const userSchema = mongoose.Schema(
},
{
timestamps: true,
toObject: { getters: true },
toJSON: { getters: true },
}
);

// add plugin that converts mongoose to json
userSchema.plugin(toJSON);

userSchema.statics.isEmailTaken = async function (email, excludeUserId) {
const user = await this.findOne({ email, _id: { $ne: excludeUserId } });
return !!user;
Expand All @@ -57,16 +59,6 @@ userSchema.methods.isPasswordMatch = async function (password) {
return bcrypt.compare(password, user.password);
};

userSchema.methods.toJSON = function () {
const user = this;
return omit(user.toObject(), ['password']);
};

userSchema.methods.transform = function () {
const user = this;
return pick(user.toJSON(), ['id', 'email', 'name', 'role']);
};

userSchema.pre('save', async function (next) {
const user = this;
if (user.isModified('password')) {
Expand Down
36 changes: 36 additions & 0 deletions src/models/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable no-param-reassign */

/**
* A mongoose schema plugin which applies the following in the toJSON transform call:
* - removes __v, createdAt, updatedAt, and any path that has private: true
* - replaces _id with id
*/
const toJSON = (schema) => {
let transform;
if (schema.options.toJSON && schema.options.toJSON.transform) {
transform = schema.options.toJSON.transform;
}

schema.options.toJSON = Object.assign(schema.options.toJSON || {}, {
transform(doc, ret, options) {
Object.keys(schema.paths).forEach((path) => {
if (schema.paths[path].options && schema.paths[path].options.private) {
delete ret[path];
}
});

ret.id = ret._id.toString();
delete ret._id;
delete ret.__v;
delete ret.createdAt;
delete ret.updatedAt;
if (transform) {
return transform(doc, ret, options);
}
},
});
};

module.exports = {
toJSON,
};

0 comments on commit f8ba3f6

Please sign in to comment.