MongoDB is a popular NoSQL database provider. Here at Rithm School we used to teach MongoDB in conjunction with Node.js. Recently, however, we have switched our Node.js curriculum to utilize PostgreSQL. For a taste of what that looks like, check out Joel's blog post or our free online curriculum on relational databases with Node/Express.
Much of the reason for this curriculum change is that relational databases, the SQL language, and ORMs can take a long time to master and are more widely-used across the industry. We decided therefore it would be best if our students could focus on mastering SQL and relational databases (the dominant database tech for the last 40 years) rather than split that time between a completely different technology that has only recently risen in popularity.
But another significant reason that I would like to argue is: MongoDB really isn't that hard to learn and master! If you're comfortable with JavaScript, JSON, and Node.js/Express.js (which all of our graduates are), then teaching yourself MongoDB can be a cakewalk. Especially if you have this blog post!
The Basics of MongoDB
MongoDB is a document store database management system that stores JSON documents in collections and queries them with JavaScript.
Technically, they are stored in a binary-JSON format called BSON, but for our purposes the documents all look and behave like JSON. …If it quacks like a duck…
A document is just a JSON object, for example:
{ "firstName": "Whiskey", "lastName": "Lane", "species": "dog", "hobbies": ["sleeping", "eating"], "age": 4 }
A collection is just a group of (hopefully) similar-looking documents. The above document could live inside of a pets
collection.
A database in MongoDB is just a set of collections, for our purposes we can think of there being one database per app.
No Schemas
MongoDB does not impose schemas onto any of the data; it is considered a schemaless database system. It's up to the application / developer to enforce what documents are supposed to look like.
To demonstrate, I could be evil and insert the following document into our pets
collection:
{ "product": "Nintendo Switch", "price": 299 }
…and MongoDB would not yell at me at all , even though that's definitely not a pet.
Thus, you've got to be careful what you're putting into your collections. The flexibility can be enjoyable (no migrations!) but the lack of structured data is a big price to pay (some would argue, too great of a price).
"Webscale"
There's a whole lot of NoSQL and MongoDB marketing and evangelism around high scalability, distributed systems, and a complicated concept called sharding, but we will leave such discussions to the dev-ops professionals for the time being!
Though, I should point out that a common criticism of companies widely adopting MongoDB is focusing too much on theoretical scalability at the expense of a better / more appropriate data model. But we at Rithm will withold judgement for now.
Installing MongoDB on OSX
The hardest part of learning MongoDB is going to be installing it. But I'll try to make it as easy as possible for you!
Installation Process
-
Use homebrew.
-
Run this command in your terminal to install MongoDB through homebrew:
brew install mongodb
-
Run the following commands in your terminal:
sudo mkdir -p /data/db sudo chmod 0755 /data/db && sudo chown $USER /data/db
These commands are just creating a data folder for MongoDB files and then setting the appropriate permissions.
Using MongoDB
-
In one terminal tab, run the command
mongod
. This ~summons the dark lord MON GOD~ runs the database server program. You can't use this terminal tab whilemongod
is running in it. MongoD stands for Mongo Daemon, a process that needs to run in the background so you can query and stuff. -
In another terminal, run the command
mongo
. This opens up the MongoDB Interactive Shell program that lets you execute queries in JavaScript from the command line. To quit the shell, the command isexit
.
The MongoDB Shell
Make a Database, a Collection, and Documents
With mongod
running, enter the MongoDB shell with the mongo
command.
We can create and connect to a new database on the fly just by using it. In the shell type use pets_db
. If you ever want to list all your databases, type show dbs
.
Now that we're connected to pets_db
, let's create a collection. How do you create a collection? Just insert a document into it, and it magically becomes a thing, just like using a database also creates it magically.
Let's insert the Whiskey document from above:
db.pets.insert({ firstName: 'Whiskey', lastName: 'Lane', species: 'dog', hobbies: ['sleeping', 'eating'], age: 4 });
Let's insert two more documents at once with the insertMany
query. This function accepts an array of documents.
db.pets.insertMany([ { firstName: 'Gandalf', species: 'owl', age: 1 }, { firstName: 'Socrates', species: 'cat' } ]);
Notice that these pet documents don't have as many fields as Whiskey. But that's okay, because there isn't any schema or anything that we need to abide by!
Querying
Let's query the list of all the pets:
Shell Command
db.pets.find();
Result
{ "_id" : ObjectId("5c08c8c8abcff34df52dded2"), "firstName" : "Whiskey", "lastName" : "Lane", "species" : "dog", "hobbies" : [ "sleeping", "eating" ], "age" : 4 } { "_id" : ObjectId("5c08c8cfabcff34df52dded3"), "firstName" : "Gandalf", "species" : "owl", "age" : 1 } { "_id" : ObjectId("5c08c8cfabcff34df52dded4"), "firstName" : "Socrates", "species" : "cat" }
Hmm that's alright but the results are all bunched up. Let's make it more readable:
Shell Command
db.pets.find().pretty();
Result
{ "_id" : ObjectId("5c08c8c8abcff34df52dded2"), "firstName" : "Whiskey", "lastName" : "Lane", "species" : "dog", "hobbies" : [ "sleeping", "eating" ], "age" : 4 } { "_id" : ObjectId("5c08c8cfabcff34df52dded3"), "firstName" : "Gandalf", "species" : "owl", "age" : 1 } { "_id" : ObjectId("5c08c8cfabcff34df52dded4"), "firstName" : "Socrates", "species" : "cat" }
That looks nicer!
Note the _id
field, a primary key that was inserted by MongoDB for us on each of the documents. This is MongoDB's way of keeping track of things, but we can also query by it if necessary.
Let's query for Whiskey and only get the first result back. Also, we'll just select her firstName
and hobbies
.
Shell Command
db.pets.findOne({ firstName: 'Whiskey' }, { firstName: 1, hobbies: 1 });
Result
{ "_id" : ObjectId("5c08c8c8abcff34df52dded2"), "firstName" : "Whiskey", "hobbies" : [ "sleeping", "eating" ] }
Notice that _id
is always included in the result, because Mongo needs it.
Now let's query animals that are less than 3 years old (sorry Whisk you poor old thing):
Shell Command
db.pets.find({ age: { $lt: 3 } });
Result
{ "_id" : ObjectId("5c08c8cfabcff34df52dded3"), "firstName" : "Gandalf", "species" : "owl", "age" : 1 }
Hopefully this syntax is relatively self-explanatory or at least intuitive once you start to play around with it. The $lt
stands for "less than" operator. Here is a list of all the other query operators, and here is a guide to common queries (MongoDB's docs even include SQL equivalents).
Using MongoDB with Node.js
Let's jump right in to using MongoDB with Node.js and Express.js. I'm assuming you have Node.js downloaded and installed of course.
We're going to build the beginning of a RESTful JSON API just to see how easy it is!
Start a Node.js Project and Install Dependencies
The following commands are all standard for making a new Node/Express project (more info here):
mkdir pets_api cd pets_api touch index.js npm init --yes npm install express mongodb body-parser
The mongodb
package is the MongoDB native driver for Node.js.
Express Server Starter Code
Here is the code for index.js
(most of it was inspired from the official docs):
const express = require('express'); const bodyParser = require('express'); const MongoClient = require('mongodb').MongoClient; // set up a connection to the server running on localhost (mongod) const mongo = new MongoClient('mongodb://localhost:27017', { useNewUrlParser: true }); // global variables app and db const app = express(); let db; // accept JSON request bodies app.use(bodyParser.json()); /** * Route handler for GET to /pets */ app.get('/pets', function(req, res, next) { db.collection('pets') // query pets .find() // find all, view result as an array .toArray(function(error, result) { // a standard Node.js "error-first" callback if (error) { return res.json({ error }); } // return the JSON of the result array return res.json(result); }); }); /** * Database connection and server set-up */ mongo.connect(function(error) { // a standard Node.js "error-first" callback if (error) { // kill express if we cannot connect to the database server console.error(error); process.exit(1); } // if we reach this line, we've made it console.log('Successfully connected to database'); // set the active database db = mongo.db('pets_db'); // standard express app.listen app.listen(3333, function() { console.log('Pets API Server listening on port 3333.'); }); });
Run the server with the command node index.js
from the terminal.
Remember that mongod
always has to be running before you start your Express server.
Now if you navigate to your browser at http://localhost:3333/pets
you should be able to see all the pets we inserted earlier!
A POST Handler to Insert Documents
What's great about MongoDB is that it stores documents in JSON format, which is also how we will POST data to our API. Let's look at a POST request handler:
app.post('/pets', function(req, res, next) { db.collection('pets').insert(req.body, function(error, result) { if (error) { return res.json({ error }); } // inserts the request body directly into database return res.json(result); }); });
Now we will make a POST request to http://localhost:3333/pets
with the following:
{ "name": "Shadowfax", "species": "horse", "age": 1000 }
Plugging it into our handy Insomnia HTTP client we get the following:
Wow! We didn't have to do any special data processing. We just sent JSON data and it went right into the database. The response format is a little "raw" right now, but it could easily be cleaned up.
If you send a GET request to /pets
again, you can see good ol' Shadowfax has been added :
[ { "_id": "5c08c8c8abcff34df52dded2", "firstName": "Whiskey", "lastName": "Lane", "species": "dog", "hobbies": ["sleeping", "eating"], "age": 4 }, { "_id": "5c08c8cfabcff34df52dded3", "firstName": "Gandalf", "species": "owl", "age": 1 }, { "_id": "5c08c8cfabcff34df52dded4", "firstName": "Socrates", "species": "cat" }, { "_id": "5c08e115ed807d3aeebcfb52", "name": "Shadowfax", "species": "horse", "age": 1000 } ]
Conclusion & Further Study
Summary
MongoDB is nice because of the ease of setting it up. If you know Node.js/Express.js and are comfortable with JSON, it should be no problem at all to dive in and start building servers. Because MongoDB handles JSON so well, there is very little processing needed if you're building a JSON API server. Also, because you don't need to define a schema, it can be a great choice as a data store for things like company take-home interview assignments (assuming they don't require a specific database). Learning MongoDB is also a great introduction to NoSQL / document stores for JavaScript developers.
Further Study
For the example code snippets above, I used old-school callback patterns to handle my async Node.js code. You can totally use ESNext features like async/await
, and I recommend reading up on the examples in their documentation.
Lastly, I also used the lowest level MongoDB driver for Node.js, but a lot of people use a tool called Mongoose, self-styled as an "ODM" (Object-Document-Mapper) which also lets you overlay schemas (at the application level) on top of your MongoDB collections. This can help with validation, etc. We also still have a section on MongoDB in our free online curriculum, which I recommend checking out!