Error from mongoose is not an instance of MongoError? - node.js

I want to capture the E11000 duplicate key error, so I deliberately insert the same user(uid is the key)
const MongoError = require('mongodb-core').MongoError
async function insertUser(uid) {
try {
await userModel.create({
"uid": uid,
"create_datetime": new Date(),
})
} catch (e) {
console.log(e.constructor)
console.log(e instanceof MongoError)
}
}
Using the Debugging tool, I can see the constructor of e is class MongoError extends Error, however the result of e instanceof MongoError is false, very strange!! anyone good advice?

I don't think this answers your actual question, but I don't believe the errors are meant to be caught like this.
Rather, you should check if
error.name==='MongoError' && error.code === 11000
which is the recommended solution of Valeri Karpov, one of the core mongoose developers himself: https://thecodebarbarian.com/mongoose-error-handling
(the article is from 2016, but I still believe it to be valid)

It seems on the MongoDB documentation that it is the good way to handle the errors (see official doc link below).
But on my side I don't even succeed to require MongoServerError, I got "MongoServerError is undefined". I installed MongoDB NodeJS Driver with "npm i mongodb" but I also have mongoose installed and I connect to a remote Atlas mongodb server. Do you know how I should require MongoServerError?
https://mongodb.github.io/node-mongodb-native/4.3/index.html#error-handling
const client = new MongoClient(url);
await client.connect();
const collection = client.db().collection('collection');
try {
await collection.insertOne({ _id: 1 });
await collection.insertOne({ _id: 1 }); // duplicate key error
} catch (error) {
if (error instanceof MongoServerError) {
console.log(`Error worth logging: ${error}`); // special case for some reason
}
throw error; // still want to crash
}

Related

firebase-admin: FieldValue.arrayUnion not working NodeJS

I'm using firebase-admin v11.2.1
I can update a title in a project using:
if (title) {
batch.update(projectRef, {
title
})
}
But I cannot add an item to an array using:
batch.update(projectRef, {
admins: admin.firestore.FieldValue.arrayUnion(`/users/${admin}`)
})
The error is being caught in a catch block, but the error comes back as an empty object
} catch (err) {
res.status(500).json({ error: err })
}
resolves as:
{
"error": {}
}
Everything I read doing what I want to do point to arrayUnion as the answer, but it's not working for me. Any help is appreciated - thank you.
Edit: Here is the way a project is modeled.
I'm on another codebase too where arrayUnion is NOT working. I get the error:
const ids = members.map((member) => member.id);
await projectRef.update({
members: admin.firestore.FieldValue.arrayUnion(...ids)
)}
error TypeError: Cannot read properties of undefined (reading 'arrayUnion')
Though I'm in another codebase where arrayUnion is working exactly like you'd expect (firebase-admin version 9.8):
if (role === 'admin') {
batch.update(organisationRef, {
invitedAdmins: admin.firestore.FieldValue.arrayUnion(userId)
})
}
So very stumped 🤔
const { FieldValue } = require('firebase-admin/firestore');
FieldValue.arrayUnion(userId)

Supabase & ExpressJS having issues with errors

I have been playing around with ExpressJS I normally use FastAPI. I can't seem to generate an error using Supabase.
I have this endpoint
app.delete('/api/delete-book/:id', cors(corsOptions), async (req, res) => {
const {data, error} = await supabase
.from('books-express')
.delete()
.match({id: req.params.id})
if (error) {
res.status(400).send({message: `ERROR! ${error.message}`})
}
if (data)
res.send({
message: `Book ID ${req.params.id} has been deleted from the database`,
})
})
This works when it comes to deleting a book via an ID. However if I enter an invalid ID I get the data if block firing.
There is no book with an ID of 222 in the database, I would expect the error to fire but its just null
Any ideas here?
This is expected behaviour; not matching any rows is not considered an error condition in postgres.
If you'd like to check if any rows were deleted, you can use something akin to (on supabase-js 2.x):
const { data, error } = await supabase.from('books-express')
.delete()
.match({id: req.params.id})
.select() // not needed on 1.x libs
if (error || data.length === 0) {
res.status(400).send({...})
}

NestJS with TypeORM - Catch PostgreSQL unique_violation error on save()

I have got a function in a service that needs to get a value of a new or already existing object from the database and save it.
Issue is that name attribute of this object needs to be unique.
I know i can just create a .some() function over all database objects to check if i can enter this new value but its probably very unoptimal.
I would like to create somethign like this (below) but i have no idea what to do next:
const newObject = await this.repository
.save({
...newObjectDto,
})
.catch(err => {
if (err instanceof QueryFailedError) {
// some code for err checking to make
// sure i only catch the unique_violation
// error and then throw error
}
});
Error code for unique_violation is 23505. You can check out the full list of error codes returned by PostgreSQL here.
In order to catch a unique constraint violation, you can do:
try {
const newObject = await this.repository
.save({
...newObjectDto,
})
}
catch(err) {
if (err instanceof QueryFailedError) {
if(err.code === '23505') {
console.log(`Unique constraint ${err.constraint} failed`);
throw err;
}
}
}

How can I get rid of this MongoDB error? mongoose MongoNotConnectedError: MongoClient must be connected to perform this operation

Please I am using the MongoDB database for my next application that is my final project for me Bootcamp and there is this error that has prevented me from making queries to my database as I always get the mongoclient not connected error.
I am using mongoose and this error started after I upgraded to the latest mui(material UI) because that is what I am using for this application. I have been trying since yesterday to fix this error as I thought it is something I could handle but till this moment it persist. It has been going from this mongoose MongoNotConnectedError: MongoClient must be connected to perform this operation and this one MongoExpiredSessionError: Cannot use a session that has ended` and it happens on every button that clicks that makes a request to the database.
Below is the code I am using to connect to MongoDB with mongoose:
import mongoose from 'mongoose';
const connection = {};
async function connect() {
if (connection.isConnected) {
console.log('already connected');
return;
}
if (mongoose.connections.length > 0) {
connection.isConnected = mongoose.connections[0].readyState;
if (connection.isConnected === 1) {
console.log('use previous connection');
return;
}
await mongoose.disconnect();
}
const db = await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('new connection');
connection.isConnected = db.connections[0].readyState;
}
async function disconnect() {
if (connection.isConnected) {
if (process.env.NODE_ENV === 'production') {
await mongoose.disconnect();
connection.isConnected = false;
} else {
console.log('not disconnect');
}
}
}
function convertDocToObj(doc) {
doc._id = doc._id.toString();
doc.createdAt = doc.createdAt.toString();
doc.updatedAt = doc.updatedAt.toString();
return doc;
}
const db = { connect, disconnect, convertDocToObj };
export default db;
I will really appreciate it so much if anybody can help me out with this problem, please. I don't know what is causing it or where it is coming from as I have tried to as much as i can to all to no0 avail
Thanks
I see that you are using the mongo URL as an environment variable with MONGODB_URI.
Do you have the dotenv module installed? Did you require('dotenv').config() in your applications? is the `.env file in the root directory of your project?
If so, if you are using MongoDB Atlas, make sure the URL in your .env file is the correct one. When you generate the connection string from MongoDB Atlas it gives a default DB in the URL string named myFirstDatabase or something like that. Change it to the DB you want to connect to.

How to drop a database with Mongoose?

I'm preparing a database creation script in Node.js and Mongoose.
How can I check if the database already exists, and if so, drop (delete) it using Mongoose?
I could not find a way to drop it with Mongoose.
There is no method for dropping a collection from mongoose, the best you can do is remove the content of one :
Model.remove({}, function(err) {
console.log('collection removed')
});
But there is a way to access the mongodb native javascript driver, which can be used for this
mongoose.connection.collections['collectionName'].drop( function(err) {
console.log('collection dropped');
});
Warning
Make a backup before trying this in case anything goes wrong!
Mongoose will create a database if one does not already exist on connection, so once you make the connection, you can just query it to see if there is anything in it.
You can drop any database you are connected to:
var mongoose = require('mongoose');
/* Connect to the DB */
mongoose.connect('mongodb://localhost/mydatabase',function(){
/* Drop the DB */
mongoose.connection.db.dropDatabase();
});
If you modify #hellslam's solution like this then it will work
I use this technique to drop the Database after my integration tests
//CoffeeScript
mongoose = require "mongoose"
conn = mongoose.connect("mongodb://localhost/mydb")
conn.connection.db.dropDatabase()
//JavaScript
var conn, mongoose;
mongoose = require("mongoose");
conn = mongoose.connect("mongodb://localhost/mydb");
conn.connection.db.dropDatabase();
HTH at least it did for me, so I decided to share =)
Tried #hellslam's and #silverfighter's answers. I found a race condition holding my tests back. In my case I'm running mocha tests and in the before function of the test I want to erase the entire DB. Here's what works for me.
var con = mongoose.connect('mongodb://localhost/mydatabase');
mongoose.connection.on('open', function(){
con.connection.db.dropDatabase(function(err, result){
done();
});
});
You can read more https://github.com/Automattic/mongoose/issues/1469
An updated answer, for 4.6.0+, if you have a preference for promises (see docs):
mongoose.connect('mongodb://localhost/mydb', { useMongoClient: true })
.then((connection) => {
connection.db.dropDatabase();
// alternatively:
// mongoose.connection.db.dropDatabase();
});
I tested this code in my own code, using mongoose 4.13.6. Also, note the use of the useMongoClient option (see docs). Docs indicate:
Mongoose's default connection logic is deprecated as of 4.11.0. Please opt in to the new connection logic using the useMongoClient option, but make sure you test your connections first if you're upgrading an existing codebase!
The difficulty I've had with the other solutions is they rely on restarting your application if you want to get the indexes working again.
For my needs (i.e. being able to run a unit test the nukes all collections, then recreates them along with their indexes), I ended up implementing this solution:
This relies on the underscore.js and async.js libraries to assemble the indexes in parellel, it could be unwound if you're against that library but I leave that as an exerciser for the developer.
mongoose.connection.db.executeDbCommand( {dropDatabase:1}, function(err, result) {
var mongoPath = mongoose.connections[0].host + ':' + mongoose.connections[0].port + '/' + mongoose.connections[0].name
//Kill the current connection, then re-establish it
mongoose.connection.close()
mongoose.connect('mongodb://' + mongoPath, function(err){
var asyncFunctions = []
//Loop through all the known schemas, and execute an ensureIndex to make sure we're clean
_.each(mongoose.connections[0].base.modelSchemas, function(schema, key) {
asyncFunctions.push(function(cb){
mongoose.model(key, schema).ensureIndexes(function(){
return cb()
})
})
})
async.parallel(asyncFunctions, function(err) {
console.log('Done dumping all collections and recreating indexes')
})
})
})
This works for me as of Mongoose v4.7.0:
mongoose.connection.dropDatabase();
2020 update
make a new file call it drop.js i.e
and put inside
require('dotenv').config()
const url = process.env.ATLAS_URI;
mongoose.connect(url, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false
});
const connection = mongoose.connection;
connection.once('open', () => {
console.log("MongoDB database connection established successfully");
})
mongoose.connection.dropDatabase().then(
async() => {
try {
mongoose.connection.close()
}
catch (err) {
console.log(err)
}
}
);
in your package.json
in your package.json
"scripts": {
"drop": "node models/drop.js",
}
run it on ur console and
To empty a particular collection in a database:
model.remove(function(err, p){
if(err){
throw err;
} else{
console.log('No Of Documents deleted:' + p);
}
});
Note:
Choose a model referring to particular schema(schema of collection
you wish to delete).
This operation will not delete collection name
from database.
This deletes all the documents in a collection.
The best way to drop your database in Mongoose depends on which version of Mongoose you are using. If you are using a version of Mongoose that 4.6.4 or newer, then this method added in that release is likely going to work fine for you:
mongoose.connection.dropDatabase();
In older releases this method did not exist. Instead, you were to use a direct MongoDB call:
mongoose.connection.db.dropDatabase();
However, if this was run just after the database connection was created, it could possibly fail silently. This is related to the connection actually be asynchronous and not being set up yet when the command happens. This is not normally a problem for other Mongoose calls like .find(), which queue until the connection is open and then run.
If you look at the source code for the dropDatabase() shortcut that was added, you can see it was designed to solve this exact problem. It checks to see if the connection is open and ready. If so, it fires the command immediately. If not, it registers the command to run when the database connection has opened.
Some of the suggestions above recommend always putting your dropDatabase command in the open handler. But that only works in the case when the connection is not open yet.
Connection.prototype.dropDatabase = function(callback) {
var Promise = PromiseProvider.get();
var _this = this;
var promise = new Promise.ES6(function(resolve, reject) {
if (_this.readyState !== STATES.connected) {
_this.on('open', function() {
_this.db.dropDatabase(function(error) {
if (error) {
reject(error);
} else {
resolve();
}
});
});
} else {
_this.db.dropDatabase(function(error) {
if (error) {
reject(error);
} else {
resolve();
}
});
}
});
if (callback) {
promise.then(function() { callback(); }, callback);
}
return promise;
};
Here's a simple version of the above logic that can be used with earlier Mongoose versions:
// This shim is backported from Mongoose 4.6.4 to reliably drop a database
// http://stackoverflow.com/a/42860208/254318
// The first arg should be "mongoose.connection"
function dropDatabase (connection, callback) {
// readyState 1 === 'connected'
if (connection.readyState !== 1) {
connection.on('open', function() {
connection.db.dropDatabase(callback);
});
} else {
connection.db.dropDatabase(callback);
}
}
Mongoose 4.6.0+:
mongoose.connect('mongodb://localhost/mydb')
mongoose.connection.once('connected', () => {
mongoose.connection.db.dropDatabase();
});
Passing a callback to connect won't work anymore:
TypeError: Cannot read property 'commandsTakeWriteConcern' of null
beforeEach((done) => {
mongoose.connection.dropCollection('products',(error ,result) => {
if (error) {
console.log('Products Collection is not dropped')
} else {
console.log(result)
}
done()
})
})
To drop all documents in a collection:
await mongoose.connection.db.dropDatabase();
This answer is based off the mongoose index.d.ts file:
dropDatabase(): Promise<any>;
mongoose.connect(`mongodb://localhost/${dbname}`, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: true,
useUnifiedTopology: true
})
.then((connection) => {
mongoose.connection.db.dropDatabase();
});
To delete a complete database, just pass the name...
This one is working perfectly fine on version 4.4
Since the remove method is depreciated in the mongoose library we can use the deleteMany function with no parameters passed.
Model.deleteMany();
This will delete all content of this particular Model and your collection will be empty.
For dropping all documents in a collection:
myMongooseModel.collection.drop();
as seen in the tests

Resources