Introduction
In this article, we are going to talk about how to use the MongoDB database with Node.js. There are couple of ways to do this, including the a popular approach - using an Object Modeling Library. Mongoose is an example of such a library in Node.js, however, we will be using the official MongoDB driver for Node.js.
In this article, we will be connecting to a MongoDB server, create documents, retrieve them from the database and finally delete some.
This will be done through a few scripts, though you'd typically integrate them with a web server/application rather than having them in standalone scripts.
What is MongoDB?
MongoDB is a cross-platform (runs on multiple operating systems), document-oriented database management system (DBMS). MongoDB is also a NoSQL database, which means it does not use SQL to perform operations on a database.
MongoDB uses documents that are in JSON-like format, known as BSON, which is the binary encoding of JSON.
It's developed as an open-source project by MongoDB Inc. under the Server Side Public License.
Node and MongoDB work very-well together, in part because Mongo uses a JavaScript engine built into the database since JavaScript is good at handling JSON objects.
Compared to other databases, such as MySQL, MongoDB is fast for storing certain types of data and can be automatically scaled. It's very simple to implement and get running.
With Mongo being a NoSQL database, it has its own way of storing data. Here are some of the constructs that make up the database structure:
- Database: The container that holds a set of collections.
- Collection: A set of documents. This is similar to a table in an SQL database. However, unlike an SQL database, a collection does not have a set structure or pre-configured data types.
- Documents: A JSON-like object. This is similar to a row in an SQL database. A JSON object may also contain child objects, an array, etc.
- _id: Mandatory unique field in every document. It separates one document from another, so we can identify each document independently. If this value is not provided, MongoDB automatically assigns a random value for the field.
Configuration of the Project
Let's start off with the project and skip the npm
prompts:
$ npm init -y
Then, let's install the official MongoDB driver:
$ npm install --save mongodb
In order to actually connect to the databse, you'll need to make sure your MongoDB service is running in the background or your development machine. Run the command mongo
on your command prompt to enter the Mongo shell:
Running the command show dbs;
will present a list of the current databases:
You can exit the shell by running the exit
command.
Unlike SQL databases, which require a database to be created before usage - there's no need to create a database or a collection beforehand. They'll automatically be created when required.
Implementing CRUD Operations
With our project initialized and MongoDB installed, we can get down to writing some CRUD logic.
Connecting to the Database
Of course, to use MongoDB in code, we need to import the module:
const mongodb = require('mongodb');
Then, let's instantiate a client:
const MongoClient = mongodb.MongoClient;
The client needs to know where to connect, so we'll supply it with a url
and dbName
:
// Connection URL
const url = 'mongodb://localhost:27017';
// Database Name
const dbName = 'userdb';
Finally, let's try connecting to the database:
// Use the connect method to create a connection w/ the database
MongoClient.connect(url, (err, client) => {
if (err) {
throw err;
return;
}
console.log('Database connection successful');
// This objects holds the refrence to the db
const db = client.db(dbName);
client.close();
});
If you have connected to the database sucessfully you should see the output:
Database connection successful
Otherwise you'll be greeted with an error message. Check if the server is up and running and if the username and password are correct in that case.
As you can see in the example, the MongoClient.connect
method takes two parameters, the URL of the database and the callback function.
The callback function has two parameters: err
and client
.
The first parameter would contain an error if there is any network issue or any other issue with connecting to the database. If there are no issues, the error will be null
.
The second parameter is the client object, which we use to interact with the database.
The db
property of the client
holds a reference to the database. To perform any action on that database, we use this reference.
Create a Document
To perform any action on the database, you have to be connected to it, obviously. With Mongo, there are two ways of inserting documents into the database. The first way is to add a single document at a time. We can use the insertOne()
method to achieve this:
const collection = db.collection('userdb');
// Insert one document
collection.insertOne({
firstName: 'john',
lastName: 'doe',
age: 21,
hobbies: [
'Reading books',
'Collecting stamps'
]
}, (err, result) => {
if (err) {
console.log(err);
return;
}
console.log(result.result);
});
The result paramater of the callback contains information about the query. It has a field called result
which looks like:
result: { n: 1, ok: 1 }
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
n
is the number of documents inserted. ok
is the he status of the command.
You don't have to excplicitly create a database named userdb
, or a collection named users
before inserting the document. The database and the collection will be automatically created.
The second method allows you to add multiple documents at once. We can use the insertMany()
method to achieve this:
// Insert multiple documents
collection.insertMany([
{
firstName: 'john',
lastName: 'doe',
age: 21,
hobbies: [
'Reading books',
'Collecting stamps'
]
}, {
firstName: 'anna',
lastName: 'dias',
age: 20,
hobbies: []
}
], (err, result) => {
if (err) {
console.log(err);
return;
}
console.log(result.ops);
});
Running this piece of code will yield:
[ { _id: 1,
firstName: 'john',
lastName: 'doe',
age: 21,
hobbies: [ 'Reading books', 'Collecting stamps' ] },
{ _id: 2,
firstName: 'anna',
lastName: 'dias',
age: 20,
hobbies: [] } ]
Since we have not defined an _id
for either of these documents, we can fetch the assigned _id
from the result['ops']
object if we ever need access to the generated _id
.
In addition to that, you can define the _id
yourself:
// Insert one document
collection.insertOne({
_id: 'someuniqueid', // Our specified ID
firstName: 'john',
lastName: 'doe',
age: 21,
hobbies: [
'Reading books',
'Collecting stamps'
]
}, (err, result) => {
if (err) {
console.log(err);
return;
}
console.log(result.result);
});
Retrieving Documents
Retrieve all Documents
First, let's look at how to fetch all documents from a collection:
// Find all documents
collection.find({}).toArray((err, docs) => {
if (err) {
throw err;
}
console.log(docs)
});
Running this piece of code will yield us:
[{ _id: 1,
firstName: 'john',
lastName: 'doe',
age: 21,
hobbies: [ 'Reading books', 'Collecting stamps' ] },
{ _id: 2,
firstName: 'anna',
lastName: 'dias',
age: 20,
hobbies: [] } ]
As you can see in the example, we have passed an empty object ({}
) as the query.
According to the documentation, the toArray()
method returns an array that contains all the documents from a cursor. The method iterates the cursor completely, loading all the documents into RAM and exhausting the cursor.
The documents fetched by the collection will be assigned to the docs
parameter in the callback function.
Find Documents with a Query Filter
The next method of finding a document is to use a query filter. For example, the following query selects the users with the first name john
:
{
'firstName': 'john'
}
And to do this in code:
collection.find({
firstName: 'john'
}).toArray((err, docs) => {
if (err) {
throw err;
}
console.log(docs)
});
This code will result in:
[{ _id: 1,
firstName: 'john',
lastName: 'doe',
age: 21,
hobbies: [ 'Reading books', 'Collecting stamps' ] } ]
Evidently, all records with the firstName
john
are returned.
Updating a Document
The next operation we are going to talk about is updating a document. To update a single document, similar to retrieving a document, we can use the updateOne()
method:
collection.updateOne(
// The query filter
{
firstName: 'john'
},
// The update values
{
$set: {
lastName: 'well',
edited: true
}
},
(err, result) => {
if (err) {
throw err;
}
console.log(result.result);
}
);
This code results in:
{ n: 1, nModified: 1, ok: 1 }
As you can see in the example, the updateOne()
method accepts three parameters. The first one is the query filter. The second one are the update values. The third one is the callback function, which accepts the error and the results as parameters.
Again, the results here notify us of the status (ok
), the number of documents selected for the update (n
) and the number of updated documents (nModified
).
n
can be greater than nModified
, if a field is updated with the value it already had.
Using this query, we have selected one document where the field firstName
is john
and we have changed the lastName
of that document to well
. Also, we have added a field called edited
and set it as true
. Notice how we have not needed to specify or follow any schema during this whole process. Mongo just accepts any data you send it.
If you are using the updateOne()
method, the query will select the first document with the matching field. If there are multiple documents with a field of the same value, using the updateMany()
method will update them all, which in some cases might not be what we want to do.
Note: If you are using the updateOne()
method, ideally the query should only select a single document. Otherwise, we cannot predict the document that might get udpated. So keep this in mind and be cautious when using a query filter that could match multiple documents.
We can also edit all the documents which satisfy the condition that the field firstName
is john
:
collection.updateMany(
// The query filter
{
firstName: 'john'
},
// The update values
{
$set: {
lastName: 'well',
edited: true
}
},
(err, result) => {
if (err) {
throw err;
}
console.log(result.result);
}
);
The updateMany()
method is similar to the updateOne()
method, except it updates all the documents that match the query filter.
Removing a Document
We can use the deleteOne()
or deleteMany()
methods to remove a document from a collection:
collection.deleteOne(
// The query filter
{
firstName: 'john'
},
(err, result) => {
if (err) {
throw err;
}
console.log(result.result);
}
);
This code results in:
{ n: 1, ok: 1 }
Again, in a similar fashion to the previous examples - the first accepted parameter is the filter query and the second parameters is the callback function. The callback function returns an error or a result.
Running this piece of code will remove a document that matches the query - in this case, a document in which the firstName
field is john
. Again, this will only delete the first document that matches the query.
You can also use the deleteMany
method to delete all the documents which are selected:
collection.deleteMany(
// The query filter
{
firstName: 'john'
},
(err, result) => {
if (err) {
throw err;
}
console.log(result.result);
}
);
Conclusion
MongoDB is a popular NoSQL, lightweight database that's really easy to implement and use with Node. We wrote a very simple Node application that interacts with a MongoDB to create, retrieve and delete collections.
As always the source code is available on GitHub.