feathers
feathers copied to clipboard
[FR] MongoDB strict JSON v1 mode (remove convert all EJSON types to JSON compatible ones)
Problem
Using setNow, softDelete and many other hooks will generate non JSON types in the database. Which causes problems when making comparisons on the client side or sharing server code with the client. Date !== string date, nor does ObjectID or Geocode.
Continuation of this 2019 issue
Instead of this:
Most of us would much rather have this: (source)
Proposed solution
Strict JSON mode on the adapter.
Example to rectify deviants
/*
Replacement for feathers-mongodb database adapter
ensures no exotic objects are created in MongoDB.
Works like middleware using inheritance.
*/
import { MongoDBService as Service } from '@feathersjs/mongodb'
import { ObjectId } from 'mongodb'
import { select } from '@feathersjs/adapter-commons'
import { Logger } from '../logger.js'
const logger = new Logger('MenDB')
const USE_STRICT_JSON = true
// Casts all values to JSON (Dates, Geoloc, ObjectId, etc)
function toStrictJson(data) {
if(USE_STRICT_JSON !== true) {
return data
}
if (data) { // Only on create, update, patch
if (!Array.isArray(data)) {
return JSON.parse(JSON.stringify(data))
} else {
return data.map(e => JSON.parse(JSON.stringify(e)))
}
} else {
return data
}
}
export default class MenDB extends Service {
// Override to create String Id's
// Uses strict json for deep cloning
_setId (ctx) {
return (item) => {
const entry = toStrictJson(item) // Object.assign({}, item)
// Generate a String ID if ID not provided
if (typeof entry[ctx.id] === 'undefined') {
entry[ctx.id] = new ObjectId().toHexString()
}
return entry
}
}
// Debugging helper
async _get(id, params = {}) {
logger.info('SUPER GET', params.tenant ? params.tenant + '/' + id : id)
return super._get(id, params)
}
// Manually sets string ID before creation
// Otherwise, MongoDB would set it to a ObjectId
async _create (data, params = {}) {
let payload = null
let promise = null
const model = await this.getModel(params)
if(Array.isArray(data)) {
payload = data.map(this._setId(this))
promise = model.insertMany(payload)
} else {
payload = this._setId(this)(data)
promise = model.insertOne(payload)
}
// const mongoResult = promise
await promise
const selectFn = select(params, this.id)
// console.log(selectFn, mongoResult, this.id, params)
const result = selectFn(payload)
// console.log(result)
return result
}
async _patch(id, _data, params) {
return super._patch(id, toStrictJson(_data), params)
}
async _update(id, data, params) {
return super._update(id, toStrictJson(data), params)
}
}
Alternative solution
We could export a strict JSON adapter, completely separately. This would negate any performance penalty for those who do not need isomorphic-friendly types.
Risks and other considerations
- The ObjectID conversion setting should be respected
- Dove has no Migration tools for NoSQL (Mongoose is no longer supported)