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.
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)
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