Это руководство потребует предварительных знаний о технике объектно-реляционного сопоставления (ORM) мангуст.
Вступление
По мере роста вашего приложения ведение журнала становится важной частью для отслеживания всего. Это особенно важно для отладки.
В настоящее время в npm уже доступны модули регистрации. Эти модули могут хранить журналы в файле в разных форматах или на разных уровнях. Мы собираемся обсудить ведение журнала API в вашем приложении Node.js Express с помощью популярного ORM Mongoose.
Итак, как бы вы могли создать плагин Mongoose, который будет вести журнал за вас более чистым способом и упростит ведение журнала API?
Что такое плагин в Mongoose?
В Mongoose схемы являются подключаемыми. Плагин похож на функцию, которую вы можете использовать в своей схеме и повторно использовать снова и снова в экземплярах схемы.
Mongoose также предоставляет глобальные плагины, которые можно использовать для всех схем. Например, мы собираемся написать плагин, который будет создавать diff
из двух jsons
и писать в mongodb
.
Шаг 1. Создание базовой модели схемы журнала
Давайте создадим базовую схему журнала со следующими шестью свойствами:
- Действие: Судя по названию, это будет курс действий API, будь то
create
update
delete
или что-то еще. - Категория: категория API. Например врачи и пациенты. Это больше похоже на класс.
- CreatedBy: пользователь, который использует API или вызвал его.
- Сообщение: здесь вы можете включить любое сообщение, которое вы хотите показать, которое будет иметь смысл или поможет во время отладки.
- Diff: это основное свойство, которое будет иметь diff двух JSON.
Вы можете добавить больше полей, если хотите, чтобы это имело смысл для вашего собственного приложения. Схема может быть изменена и обновлена в соответствии с требованиями.
Вот наша модель: models/log.js
const mongoose = require('mongoose') const Schema = mongoose.Schema const { ObjectId } = Schema const LogSchema = new Schema({ action: { type: String, required: true }, category: { type: String, required: true }, createdBy: { type: ObjectId, ref: 'Account', required: true }, message: { type: String, required: true }, diff: { type: Schema.Types.Mixed }, },{ timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' }, }) LogSchema.index({ action: 1, category: 1 }) module.exports = mongoose.model('Log', LogSchema)
Шаг 2. Напишите функцию, чтобы узнать разницу между двумя JSON.
Итак, следующий шаг - вам нужна функция многократного использования, которая на лету создаст diff
из двух JSON.
Назовем это diff.js
const _ = require('lodash') exports.getDiff = (curr, prev) => { function changes(object, base) { return _.transform(object, (result, value, key) => { if (!_.isEqual(value, base[key])) result[key] = (_.isObject(value) && _.isObject(base[key])) ? changes(value, base[key]) : value }) } return changes(curr, prev) }
Я использовал популярную библиотеку lodash
, чтобы обеспечить ту же функциональность.
Давайте разберем вышеуказанную функцию и посмотрим, что происходит:
- _.transform: это альтернатива
.reduce
для массивов. По сути, он будет перебирать ваши объектыkeys
иvalues
. Он предоставляетaccumulator
, который является первым аргументом.result
- это аккумулятор, и он изменяемый. - _.isEqual: выполняет глубокое сравнение двух значений, чтобы определить, эквивалентны ли они.
isEqual: этот метод поддерживает сравнение массивов, буферов массивов, логических значений, объектов даты, объектов ошибок, карт, чисел, объектов
Object
, регулярных выражений, наборов, строк, символов и типизированных массивов.Object
объекты сравниваются по их собственным, а не унаследованным, перечисляемым свойствам. Функции и узлы DOM сравниваются на строгое равенство, т.е.===
.
Здесь мы перебираем каждое свойство и значение объекта и сравниваем его с нашим старым / предыдущим объектом.
Если value
текущего объекта не равно значению того же свойства в предыдущем объекте: base[key]
и если это значение является самим объектом, мы вызываем функцию changes
рекурсивно, пока она не получит значение который, наконец, будет сохранен в result
как result[key] = value
.
Шаг 3. Создайте плагин для использования diff и сохраните его в базе данных.
Теперь нам нужно отслеживать предыдущие document
в базе данных и создать diff
перед сохранением в mongodb
.
const _ = require('lodash') const LogSchema = require('../models/log') const { getDiff } = require('../utils/diff') const plugin = function (schema) { schema.post('init', doc => { doc._original = doc.toObject({transform: false}) }) schema.pre('save', function (next) { if (this.isNew) { next() }else { this._diff = getDiff(this, this._original) next() } }) schema.methods.log = function (data) { data.diff = { before: this._original, after: this._diff, } return LogSchema.create(data) } } module.exports = plugin
В Mongoose доступны разные крючки. На данный момент нам нужно использовать методы init
и save
, доступные в схеме.
this.isNew()
: Если вы создаете новый документ, просто верните next()
промежуточное ПО.
In schema.post('init')
toObject()
:
doc._original = doc.toObject({transform: false})
Mongoose Model
s наследуются от Document
s, у которых есть toObject()
метод. Он преобразует document
в Object()
, а transform:false
не позволяет преобразовать возвращаемый объект.
Шаг 4: Использование - Как использовать в API express.js
В вашем основном server.js
или app.js
:
Инициализируйте глобальный плагин, чтобы он был доступен для всех схем. Вы также можете использовать его для конкретной схемы, инициализировав его в модели схемы.
const mongoose = require('mongoose') mongoose.plugin(require('./app/utils/diff-plugin'))
Вот базовый пример user
API обновления:
const User = require('../models/user') exports.updateUser = (req, res, next) => { return User.findById(req.params.id) .then(user => { if (!user) throw new Error('Target user does not exist. Failed to update.') const { name } = req.body if (name) user.name = name return user.save() }) .then(result => { res.json(result) return result }) .catch(next) .then(user => { if (user && typeof user.log === 'function') { const data = { action: 'update-user', category: 'users', createdBy: req.user.id, message: 'Updated user name', } return user.log(data) } }).catch(err => { console.log('Caught error while logging: ', err) }) }
Заключение
В этом руководстве вы узнали, как создать плагин Mongoose и использовать его для регистрации changes
в вашем API. Вы можете сделать гораздо больше с помощью плагинов, чтобы создать надежное приложение узла.
Вот ресурсы, чтобы узнать больше об использовании Mongoose и плагинов:
- Руководство 80/20 по плагинам мангуста: http://thecodebarbarian.com/2015/03/06/guide-to-mongoose-plugins
- Https://mongoosejs.com/docs/plugins.html
Я надеюсь, что вы найдете это руководство полезным, не стесняйтесь обращаться к нам, если у вас есть какие-либо вопросы.
Подпишитесь на Шайлеша Шехавата, чтобы получать уведомления, когда я публикую новый пост.
Не стесняйтесь хлопать в ладоши, если считаете, что это стоит прочитать!
Первоначально опубликовано на 101node.io 2 сентября 2018 г.