I'm staggered by how much I'm struggling to delete a document in MongoDB from Express JS. I'm finding the mix of documentation and methods including deleteOne, findByIdAndRemove, some tutorials say you need to declare an ObjectId, and some don't. Madness.
Anyway, below is my code. I have a function to connect to the database:
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('database-name');
await operations(db);
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error });
}
}
And then the below is my delete command:
app.delete('/api/reports/delete-report/:id', async (req, res) => {
//call withDB function above
withDB(async (db) => {
//delete command
const result = await db.collection('reports').deleteOne( { _id : new MongoClient.ObjectId(req.params.id) } );
//get reports
const reportInfo = await db.collection('reports').find().toArray()
//put returned reports into the result provided
res.status(200).json(reportInfo);
}, res);
});
For my troubles I get the message 'Error connecting to db'. If I make the delete command:
const result = await db.collection('reports').deleteOne( { _id : req.params.id } );
I just get the contents of the database returned, but there's no deletion.
The issue is:
new MongoClient.ObjectId(req.params.id)
You don't want to create another mongoclient. It needs to be
new ObjectId(req.params.id)
and make sure you import that class:
const { MongoClient, ObjectId } = require('mongodb');
Related
I'm trying send data from a MongoDB server to the client on a GET request. I'm using an express server and want to send all the documents in a specific collection when a GET request is received.
I know MongoDB is asynchronous, so I query an asynchronous function with a promise. I am able to log to console all the documents in the MongoDB Collection but when I return it the data becomes undefined. I have a database called 'testDB' and I want to return all the documents in a specific collection ('testCollection').
app.get('/getData', (req, res) => {
returnData().then(result => {
console.log(result); //This logs undefined
res.send(result); //This sends undefined
})
});
async function returnData() {
const uri = "mongodb+srv://" + "username" + ":" + "password" + "#" + "connection url" + "/?retryWrites=true&w=majority";
//Connect to the database and return all documents in the collection
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
});
try {
await client.connect();
const database = client.db('testDB');
const collection = database.collection('testCollection');
const query = {};
const options = {};
const cursor = collection.find(query, options);
await cursor.toArray().then((docs) => {
console.log(docs); // <- This works and logs all the data to console
return docs;
});
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
EDIT:
I tried this and it returns undefined.
try {
await client.connect();
const database = client.db('testDB');
const collection = database.collection('testCollection');
const query = {};
const options = {};
const cursor = collection.find(query, options);
await cursor.toArray().then((docs) => {
return cursor.toArray();
});
} catch (e) {
console.error(e);
} finally {
await client.close();
}
I tried this and I get [MongoPoolClosedError]: Attempted to check out a connection from closed connection pool
try {
await client.connect();
const database = client.db('testDB');
const collection = database.collection('testCollection');
const query = {};
const options = {};
const cursor = collection.find(query, options);
return cursor.toArray();
} catch (e) {
console.error(e);
} finally {
await client.close();
}
Don't open and close the mongo client on each GET. Connect once and export that connection to your other modules. In terms of the Express request/response. You hand it off something like below.
app.get('/getData', myAsyncFunction)
async function index (req, res) {
let r = await db.collection("testDB").find(query).toArray();
return res.send(r);
}
I would just like to simply delete a record from a dynamically displayed list. I've tried every permutation of the backend code and ai just can't get it to work.
The backend is called like this:
async function deletePost() {
setLoading(true)
try {
await axios.delete(`/api/delete/${id}`)
alert("Post deleted")
}
catch (err) {
// notify user that something went wrong
console.log(err)
}
finally {
setLoading(false)
}
setLoading(false)
}
And /api/delete/${id} looks like this:
import { connectToDatabase } from "util/mongodb"
export default async (req, res) => {
const { id } = req.query;
console.log(id)
try {
const { db } = await connectToDatabase()
await db.collection("users").deleteOne({'_id': `ObjectId("${id}")`})
res.sendStatus(200).send({ done: true })
}
catch (error) {
return res.json({ error })
}
}
The console log shows the correct post id, the alert in the 'try' frontend code displays, but the dam post just wont delete. Can anyone offer any advice please? I have tried ".deleteOne({'_id': id})" but that does nothing either.
I believe you are searching for a string, instead of the ObjectId, so no documents match.
You can fix it by converting to ObjectId and then using the value converted, .deleteOne.
var ObjectId = require('mongodb').ObjectId;
const { id } = req.query;
const convertedObjectId = new ObjectId(id);
db.collection("users").deleteOne({_id: convertedObjectId })
Actual example from documentation:
try {
db.orders.deleteOne( { "_id" : ObjectId("563237a41a4d68582c2509da") } );
} catch (e) {
print(e);
}
Reference: MongoDB Documentation - Delete One.
Fixed problem:
app.delete("/cars/:id", async (req, res) => {
const carsId = req.params.id;
const query = { _id: ObjectId(carsId) };
const result = await carCollection.deleteOne(query);
res.send(result);
});
I am following MongoDB Atlas Blog tutorial to add info into mongodb but I am getting the above mentioned error. I have tried to resolve this error, even tried to then but still getting the same issue...
Following is my connection file
async function main(){
/**
* Connection URI. Update <username>, <password>, and <your-cluster-url> to reflect your cluster.
* See https://docs.mongodb.com/ecosystem/drivers/node/ for more details
*/
const uri = "mongodb+srv://an:abc#abc-2yzxs.mongodb.net/test?retryWrites=true&w=majority";
const client = new MongoClient(uri);
try {
// Connect to the MongoDB cluster
await client.connect();
await createListing(client,
{
msg: client.msg
}
);
return client;
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
main().catch(console.error);
async function createListing(client, newListing){
const result = await client.db("mydb").collection("mycollection").insertOne(newListing);
console.log(`New listing created with the following id: ${result.insertedId}`);
}
and following is my schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const mySchema = new Schema(
{
msg: {
type: String
}
}
);
let a = mongoose.model("mycollection", mySchema);
module.exports = a;
My Controller:
const Log = require('../models/mySchema');
require('../connection');
function createListing(data){
let Log = new Log({ msg: data});
var err = Log.save();
console.log("err is : ", err)
}
exports.createListing = createListing;
this is how I'm calling from server file
let log = require('./controllers/myController');
log.createListing(data);
Personally I wouldn't use mongoose, although you tutorial does. I just use mongodb.
import { connect } from 'mongodb'
async function main() {
// try-catch
const MONGO = 'mongodb+srv://an:abc#abc-2yzxs.mongodb.net/testretryWrites=true&w=majority'
const client = await connect(MONGO, {
useNewUrlParser: true,
useUnifiedTopology: true })
const mongo = client.db()
const Log = mongo.collection('Log')
await Log.insertOne({ message: 'test' })
}
I know it's different from the problem you have but I just don't know why mongoose is needed. It's not really.
you need to use await before Log.save() to log actual value returning by the function instead of promise pending you are getting.
async function createListing(data){
let Log = new Log({ msg: data});
let err = await Log.save();
console.log("err is : ", err)
}
I'm not getting into the callbacks for either the save() or find() methods. It's also not getting into the callback for mongoose.connect. Any idea why?
Here's my code:
mongoose.connect("mongodb://localhost/blah", {
useMongoClient: true,
}, function (err) {
console.log('connect to database'); // Not called
if (err) {
throw err;
}
});
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
var u = new MyUserModel();
u.author = 'authorname';
u.save(function(err){
console.log("saved") // not called
if (err) console.log(err);
});
MyUserModel.find({}, function (err,docs) {
console.log("found"); // not called
console.log(docs);
});
You need to "await" the connection before doing anything. It is an "async" function, and like all async functions you need to await their resolution before continuing other code that depends on the result. In this case the "connection" is going to be needed by later actions with the database.
Best done in modern environments with async/await:
// setup part
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
// async parts - wrapped in a self executing closure with the async keyword
(async function() {
try {
const conn = await mongoose.connect("mongodb://localhost/blah", {
useMongoClient: true,
});
console.log('database connected');
// Add a user and wait for it to save
var u = new MyUserModel();
u.author = 'authorname';
await u.save();
console.log("saved");
// find the documents and await that too
let docs = await MyUserModel.find();
console.log(JSON.stringify(docs,undefined,2));
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})();
Or by chaining promises in older nodejs versions:
// setup part
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
// Async part
mongoose.connect("mongodb://localhost/blah", { useMongoClient: true })
.then( () => {
var u = new MyUserModel();
u.author = 'authorname';
return u.save();
})
.then( () => MyUserModel.find() )
.then( docs => {
console.log(JSON.stringify(docs,undefined,2));
mongoose.disconnect();
})
.catch( e => console.error(e) );
Bottom line is that you need to wait for completion of all async calls and .connect() is now an async call in modern mongoose releases. It really should have always been handled like this, but now you "must" handle like this.
I've tried to find a solution to this question in: http://mongodb.github.io/node-mongodb-native/
However, I could not find a solution to listing all available MongoDB databases from a Node.js app.
Use db.admin().listDatabases.
You can do this now with the Node Mongo driver (tested with 3.5)
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost:27017/";
const client = new MongoClient(url, { useUnifiedTopology: true }); // useUnifiedTopology removes a warning
// Connect
client
.connect()
.then(client =>
client
.db()
.admin()
.listDatabases() // Returns a promise that will resolve to the list of databases
)
.then(dbs => {
console.log("Mongo databases", dbs);
})
.finally(() => client.close()); // Closing after getting the data
Only admins can see all the database. So connect to mongodb database with admin creds then create an admin instance by await db.admin(), then list down all databases await adminDB.listDatabases()
const MongoClient = require('mongodb').MongoClient;
let client = await MongoClient.connect(process.env.MONGO_DB_URL);
const db = await client.db(process.env.DEFAULT_DB_NAME);
let adminDB = await db.admin();
console.log(await adminDB.listDatabases());
If you are using mongoose, the below code can be used.
import mongoose from 'mongoose';
mongoose.set('strictQuery', true);
mongoose.connect('mongodb://localhost:27017/mydb', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
// Check DB Connection
db.once('open', () => {
(async () => {
const data = await mongoose.connection.db.admin().command({
listDatabases: 1,
});
console.log(data);
})();
console.log('Connected to MongoDB');
});
// Check for DB errors
db.on('error', (err) => {
console.log('DB Connection errors', err);
});
export default mongoose;
If you want to get the database list on your other functions, make sure the connection is established first and also make sure the user has admin access and then just do the below query. This is a sample from my API router.
// Get all databases
router.get('/database/get', async (req, res) => {
try {
const data = await mongoose.connection.db.admin().command({
listDatabases: 1,
});
if (data && data !== null) {
res.status(200).send({ data: data });
return;
}
res.status(200).send({ data: null, message: 'Data not found' });
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
res.status(500).send(e.message);
}
});
*Its difficult to get list by db.admin().listDatabases, below code will work fine in nodejs *
const { promisify } = require('util');
const exec = promisify(require('child_process').exec)
async function test() {
var res = await exec('mongo --eval "db.adminCommand( { listDatabases: 1 }
)" --quiet')
return { res }
}
test()
.then(resp => {
console.log('All dbs', JSON.parse(resp.res.stdout).databases)
})
test()