Using only JsonSchema with Mongoose possible? - node.js

I have an api using express, mongodb and I use AJV validation to validate the incoming requests.
//JSONSchema
var recordJsonSchema = {
type: "object",
properties: {
name: { type: "string" },
idNumber: { type: "number" },
content: { type: "string" }
},
required: ['name','idNumber']
}
And I'd use this JSON schema to validate incoming requests like so.
app.post('/record', (req,res) => {
let errors = ajv.inspect(req.body, recordJsonSchema)
return errors ? res.send(errors) : res.send(this.handler(req));
})
This works fine and is very fast. I also like JsonSchema since it follows OpenAPI standards.
Unfortunately, in order to read/write to mongo via mongoose I also need to create a MongoSchema for Record. They are very similar but a bit different in how they handle required fields etc.
var recordSchema = new Schema({
name: { type: "string", required: true },
idNumber: { type: "number", required: true },
content: { type: "string" }
})
So for my model of Record I have two schemas now. One for JSONschema and one for handling Mongo read/writes.
I'm looking for a way to cut MongoSchema, any suggestions?

Maybe this, seems like it imports your ajv schema from the entry and place it in the mongoose schema. https://www.npmjs.com/package/mongoose-ajv-plugin

I have faced with same problem. I think in new mongo 4.4 we can load ajv schema directly to mongodb https://docs.mongodb.com/manual/reference/operator/query/jsonSchema/

Related

Mongoose version Key and _id set to false not working npm version 5.12.9

Hello guys I am using Mongoose for interacting with MongoDB, Actually I want to turn off _id and _v for not coming for my each document but as you can see I do set those options to false but when I run my code and get data from the database using .find() method I can see those fields are still coming. Can anyone tell me how can I fix this ? Thanks in Advance.
const clanData = new mongoose.Schema(
{
isWarLogPublic: {
type: mongoose.SchemaTypes.Boolean,
},
tag: {
type: mongoose.SchemaTypes.String,
},
locationID: {
type: mongoose.SchemaTypes.Number,
},
},
{
versionKey: false,
},
{ _id: false }
);
module.exports = mongoose.model("ClanData", clanData);

throw new TypeError(`Invalid schema configuration: \`${name}\` is not ` +

I wrote the below schema. But while running it gives me an error -- throw new TypeError(Invalid schema configuration: \${name}` is not ` + --- can someone help me why does this error comes?. Below shown is my schema, can someone figure out if my schema have some mistakes.
$
jsonSchema: {
bsonType: "object",
properties: {
name: {
bsonType: "string",
description: "must be a string"
},
teacherId: {
bsonType: "objectId",
description: "must be a Object ID"
}
}
}
You are using the MongoDB native syntax to define your schema. If you are using mongoose, mongoose has its own syntax. Please refer to mongoose documentation. For your schema it would be:
var studentSchema = new mongoose.Schema({
name: { type: String },
teacherId: { type: mongoose.Schema.Types.ObjectId }
})
Mmm how I resolved this was to use the Types defined within the Schema import. For some reason importing the Types module directly didn't work -- well for Map at least.
import { Document, Schema, model, Query, Model } from "mongoose";
{
...
metadata: { type: Schema.Types.Map, of: String, required: false },
}
"name" is not a valid attribute anymore in mongoose schemas. Replace it with "type" it will work.
Oh man this is related to wrong name of the schema fields in my case:
participants: [
{
typeof: Types.ObjectId,
ref: "User",
},
]
and I got same error as you. I was so struggled and this typeof - type f* VS code I'm using it at the moment cause this computer can't deal with jet brains, :(

What is the best way to handle querystrings in an express api?

I am a beginner nodejs developer, working on developing an express rest api with optional query params.
For example consider the following schema for a user:
phone: {
countryCode: String,
number: phoneType
},
phoneVerified: { type: Boolean, default: false },
emailVerified: { type: Boolean, default: false },
rating: Number,
balance: { type: Number, default: 0 },
occupation: String,
gender: { type: String, enum: genders },
I want to expose this resource at /users and allow querying through optional query strings.
For ex, /users?emailVerified=true&phoneverified=false&gender=male&occupation=plumber&limit=10
This should return all the users which satisfy the criteria, while keeping almost all of the options optional.
What is the best way to do this in a maintenable and futureproof way?
Appraoch 1: My first approach was to use if blocks to check which parameters exist in the query and build mongoose queries accordingly, but that looks ugly and very hard to read.
queryObj = {};
if (req.query.occupation) {
queryObject = {
...queryObject,
occupation: req.query.occuption
};
}
if (req.query.phoneVerified) {
queryObject = {
...queryObject,
phoneVerified: req.query.phoneVerifed
};
}
const users = await User.find(queryObject);
I also found the querymen package which looks promising. If someone experinced could guide me as to what is the best practice? Thanks in advance
you are doing it in right way. you can also try
queryObject = {};
if (req.query.params.occupation) {
queryObject.occupation= req.params.occuption
}
if (req.params.phoneVerified) {
queryObject.phoneVerified= req.params.phoneVerifed
}
const users = await User.find(queryObject);
you can add new properties to json using "."

Unique property fails in Sails.js

The following code represents an Account Model in Sails.js v0.9.4 .
module.exports = {
attributes: {
email: {
type: 'email',
unique: true,
required: true
},
password:{
type: 'string',
minLength: 6,
maxLength: 15,
required:true
}
}
};
When I send two POSTS and a PUT request via Postman to localhost:8080/account, the unique property of the email fails.
Specifically, I send the following HTTP requests from Postman:
POST http://localhost:8080/account?email=foo#gmail.com&password=123456
POST http://localhost:8080/account?email=bar#gmail.com&password=123456
PUT http://localhost:8080/account?id=1&email=bar#gmail.com
GET http://localhost:8080/account
The last GET request shows me:
[
{
"email": "bar#gmail.com",
"password": "123456",
"createdAt": "2013-09-30T18:33:00.415Z",
"updatedAt": "2013-09-30T18:34:35.349Z",
"id": 1
},
{
"email": "bar#gmail.com",
"password": "123456",
"createdAt": "2013-09-30T18:33:44.402Z",
"updatedAt": "2013-09-30T18:33:44.402Z",
"id": 2
}
]
Should this happen?
*For those who don't know, Waterline generates by default an id which automatically increments in every insertion.
This is because your schema is not updated in your disk database (".tmp/disk.db").
You need to shutdown sails, drop your DB and restart sails.
The DB will be reconstruct with your good schema.
Attention : the data will be drop too !
If you want keep your data, you can just update the schema part of ".tmp/disk.db".
What I have doing to keep data and rebuild schema by sails.js :
copy ".tmp/disk.db"
clean ".tmp/disk.db"
shutdown sails.js
start sails.js
-> the database is empty and the schema is updated
copy old "counters" part
copy old "data" part
You must have this in your schema (file ".tmp/disk.db" -> "schema" part) for the unique field :
"xxx": {
"type": "string",
"unique": true
},
I hope this help you.
I ran into this same issue. To solve it, you have to avoid using the 'disk' ORM adapter. For some reason it appears that it doesn't support uniqueness checks.
Other adapters such as mongo and mysql should support uniqueness checks, so this shouldn't be an issue outside of development.
For the course of development, change the default adapter in config/adapters.js from 'disk' to 'memory'. Should look like this:
module.exports.adapters = {
// If you leave the adapter config unspecified
// in a model definition, 'default' will be used.
'default': 'memory',
// In-memory adapter for DEVELOPMENT ONLY
memory: {
module: 'sails-memory'
},
...
};
I'm not certain this is the issue, but have you added schema:true to your models and adapters?
My mongo adapter config looks like this:
module.exports.adapters = {
'default': 'mongo',
mongo: {
module: 'sails-mongo',
url: process.env.DB_URL,
schema: true
}
};
And my User model looks like this (trimmed a bit):
module.exports = {
schema: true,
attributes: {
username: {
type: 'string',
required: true,
unique: true
}
//...
}
};
There is no need to delete current database to solve this, in stead change the waterline migrate option from safe to alter. This way the underlying database will adapt this setting.
I wouldn't recommend migrate: alter in a production environment, though. ;)
Here is my /config/local.js:
module.exports = {
...
models: {
migrate: 'alter'
},
}
According to the official documentation of sails
You should configure the option "migrate" in "alter" to create the schemas with their indexes
There's nothing wrong with adding or removing validations from your
models as your app evolves. But once you go to production, there is
one very important exception: unique. During development, when your
app is configured to use migrate: 'alter', you can add or remove
unique validations at will. However, if you are using migrate: safe
(e.g. with your production database), you will want to update
constraints/indices in your database, as well as migrate your data by
hand.
http://sailsjs.com/documentation/concepts/models-and-orm/validations
var InvoiceSchema = new Schema({
email: {type: 'email', required: true}
name : {type: String}
});
InvoiceScheme({email: 1}, {unique: true});
Set Uniquee In Nodejs

How to sort array of embedded documents via Mongoose query?

I'm building a node.js application with Mongoose and have a problem related to sorting embedded documents. Here's the schema I use:
var locationSchema = new Schema({
lat: { type: String, required: true },
lon: { type: String, required: true },
time: { type: Date, required: true },
acc: { type: String }
})
var locationsSchema = new Schema({
userId: { type: ObjectId },
source: { type: ObjectId, required: true },
locations: [ locationSchema ]
});
I'd like to output the locations embedded in the userLocations documented sorted by their time attribute. I currently do the sorting in JavaScript after I retrieved the data from MongoDb like so:
function locationsDescendingTimeOrder(loc1, loc2) {
return loc2.time.getTime() - loc1.time.getTime()
}
LocationsModel.findOne({ userId: theUserId }, function(err, userLocations) {
userLocations.locations.sort(locationsDescendingTimeOrder).forEach(function(location) {
console.log('location: ' + location.time);
}
});
I did read about the sorting API provided by Mongoose but I couldn't figure out if it can be used for sorting arrays of embedded documents and if yes, if it is a sensible approach and how to apply it to this problem. Can anyone help me out here, please?
Thanks in advance and cheers,
Georg
You're doing it the right way, Georg. Your other options are either to sort locations by time upon embedding in the first place, or going the more traditional non-embedded route (or minimally embedded route so that you may be embedding an array of ids or something but you're actually querying the locations separately).
This also can be done using mongoose sort API as well.
LocationsModel.findOne({ userId: theUserId })
// .sort({ "locations.time": "desc" }) // option 1
.sort("-locations.time") // option 2
.exec((err, result) => {
// compute fetched data
})
Sort by field in nested array with Mongoose.js
More methods are mentioned in this answer as well
Sorting Options in mogoose
Mongoose Sort API

Resources