By the end of this lesson you should be able to...
- Compare and contrast Mongoose with Mongo
- Create Mongoose schemas & models
- Integrate Mongoose with Express
- Build out
index
,new
, andcreate
routes with Mongoose & Express
Mongoose is an ODM, an Object Document Mapper. It maps documents in a database to JavaScript Objects, making modifying the database possible with easy to use JavaScript helper methods.
"Let's face it, writing MongoDB validation, casting and business logic boilerplate is a drag. That's why we wrote Mongoose."
—Creators of Mongoose.
- Schema: Similar to an object constructor, a Schema is a diagram or blueprint for what every object in the noSQL database will contain. Here's an example of a simple Address Book noSQL database schema:
var ContactSchema = new Schema({
firstName: String,
lastName: String,
address: String
phoneNumber: Number,
email: String,
professionalContact: Boolean
});
With the above Schema, we can expect all of our Address Book entries would have a first name, last name, address, and email address in the form of Strings. We can count on the phoneNumber to always be accepted, stored, and returned as a number. Lastly, the boolean value of Professional Contact will always be a true
or false
- Model: A model is a Schema that has been 'activated' with real data and is performing actions such as reading, saving, updating, etc.
var Contact = mongoose.model('Contact', ContactSchema);
"In mongoose, a schema represents the structure of a particular document, either completely or just a portion of the document. It's a way to express expected properties and values as well as constraints and indexes. A model defines a programming interface for interacting with the database (read, insert, update, etc). So a schema answers "what will the data in this collection look like?" and a model provides functionality like "Are there any records matching this query?" or "Add a new document to the collection". ""
-Peter Lyons Apr 8 '14 at 23:53
In other words, a Schema
is like an empty portion plate:
That your lunchlady (the Model
) will be filling to create new Instances
of your meal:
From the console:
mkdir mongoose_project
cd mongoose_project
npm init -y
npm install --save mongoose
We need to make sure MongoDB is running. From the console, enter this command:
mongod
Create a new Javascript file by typing touch index.js
into the terminal.
Let's require mongoose and connect to our database.
var mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/test");
Let's create a Book
model. A Book
has a few different characteristics: title
, author
, and description
.
To create a Book
model we have to use a Schema:
var Schema = mongoose.Schema;
var BookSchema = new Schema({
title: String,
author: String,
description: String
});
and finally create the model
var Book = mongoose.model('Book', BookSchema);
Check the docs to see all the different datatypes we can use in a Schema.
###Create: Building and Creating Documents
If you want to build a new Book
, you can just do the following:
var book = new Book({title: "Alice's Adventures In Wonderland"});
Then you can still edit it before saving.
book.author = "Lewis Carroll";
Once you're done building you can save the book.
book.save();
Note: you can pass a function that will be called (aka a callback) once the
book
is done being saved.
If you want to build & save in one step you can use .create
. Also we'll pass it a callback to execute once it's done creating the book.
Book.create({title: "The Giver"}, function (err, book) {
console.log(book);
});
###Read
We can find books by any field, including author
:
Book.find({author: "Lewis Carroll"}, function (err, books) {
console.log(books);
});
We can find ALL by specifying any fields
we're filtering for, aka an empty object:
Book.find({}, function(err, books){
console.log(books);
});
Try out some of the other find methods.
.findOne();
.findById();
Reference the docs for more info on what you can do with Mongoose queries and models (use the left-hand side-bar).
###Destroy Removing a Document is as simple as Building and Creating.
Using the remove method:
Book.remove({ title: "The Giver" }, function(err, book) {
if (err) { return console.log(err) };
console.log("removal of " + book.title + " successful.");
});
Other removal methods include:
findByIdAndRemove();
findOneAndRemove();
Well, that's nice. But let's see how mongoose plays with express by building a reminders app. called reminders
Lets create a brand new express application and grab up all the dependencies we'll need
$ mkdir reminders
$ cd reminders
$ npm init -y
$ npm install --save express ejs body-parser mongoose
$ touch index.js
The dependencies we'll be using for this app are:
express
- web Frameworksejs
- view enginebody-parser
- allows us to get parameter values from formsmongoose
- our Mongo ODM
Let's first start by defining our schema, models and creating some seed data.
Folders/files:
$ mkdir db
$ mkdir models
$ touch models/reminder.js
$ touch db/seed.js
We will define the structure of our database using schemas
In models/reminder.js
:
// requiring mongoose dependency
var mongoose = require('mongoose');
// defining schema for reminders
var ReminderSchema = new mongoose.Schema({
title: String,
body: String,
createdAt: { type : Date, default: new Date() }
});
// define the model
var Reminder = mongoose.model("Reminder", ReminderSchema);
// export the model to any files that `require` this one
module.exports = Reminder;
Great! Now that we have an interface for our models, let's create a seed file so we have some data to work with in our application.
In db/seed.js
:
var mongoose = require('mongoose');
var conn = mongoose.connect('mongodb://localhost/reminders');
var Reminder = require("../models/reminder");
Reminder.remove({}, function(err) {
if (err) {
console.log("ERROR:", err);
}
})
var reminders = [
{
title: "Cat",
body: "Figure out his halloween costume for next year"
},
{
title: "Laundry",
body: "Color-code socks"
},
{
title: "Spanish",
body: "Learn to count to ten to impress the ladies"
}
];
Reminder.create(reminders, function(err, docs) {
if (err) {
console.log("ERROR:", err);
} else {
console.log("Created:", docs)
mongoose.connection.close();
}
});
Feel free to personalize the reminders to suit your own interests and errands.
Now run the seed file in order to add these default values to our Database, by typing node db/seed.js
in the terminal.
Now we've got all of our models and seed data set. Let's start building out the reminders application. Let's update our main application file to include the dependencies we'll need.
In index.js
:
// Dependencies
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
// Configuration
mongoose.connect('mongodb://localhost/reminders');
process.on('exit', function() { mongoose.disconnect() }); // Shutdown Mongoose correctly
app.set("view engine", "ejs"); // sets view engine to EJS
app.use(bodyParser.json()); // allows for parameters in JSON and html
app.use(bodyParser.urlencoded({extended:true}));
app.use(express.static(__dirname + '/public')); // looks for assets like stylesheets in a `public` folder
var port = 3000; // define a port to listen on
// Controllers
var remindersController = require("./controllers/remindersController");
// Routes
app.get("/reminders", remindersController.index);
// Start server
app.listen(port, function() {
console.log("app is running on port:", port);
});
Challenge: Build out your own server file. Make sure it works by running it with
node
ornodemon
.
As we can see on the last line of the code above, we have just one route. It's using remindersController.index
as a callback, but it hasn't been defined yet. Let's define it now:
$ mkdir controllers
$ touch controllers/remindersController.js
And then add this into our new remindersController.js file:
var Reminder = require("../models/reminder")
var remindersController = {
index: function(req, res) {
Reminder.find({}, function(err, docs) {
res.render("reminders/index", {reminders: docs});
});
}
}
module.exports = remindersController;
Now we're referncing an ejs view that doesn't exist yet. Lets create that now, as well as our layout view:
$ mkdir views
$ touch views/layout.ejs
$ mkdir views/reminders
$ touch views/reminders/index.ejs
In views/layout.ejs
:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/css/styles.css">
</head>
<body>
<header>
<h1><a href="/reminders">Reminder.ly</a></h1>
<a href="/reminders/new">+ Reminder</a>
</header>
<hr>
<!-- the below `triple-stash`, avoid escaping any html -->
<%- body %>
</body>
</html>
In views/reminders/index.ejs
:
<ul>
<% for(var i=0; i< reminders.length; i++) {%>
<li class="reminder">
<a class="reminder-title" href="<%= '/reminders/'+reminders[i]._id %>"><%= reminders[i].title %>:</a>
<span class="reminder-body"><%= reminders[i].body %></span>
</li>
<% } %>
</ul>
Challenge: Create these template files in your own project. What happens when you try to go to
localhost:3000/reminders
?
We'll need to update our routes in index.js
to add #new
and #create
actions.
app.get("/reminders", remindersController.index);
app.get("/reminders/new", remindersController.new);
app.post("/reminders", remindersController.create);
Our views/reminders/new.ejs should look something like this:
<h2>Create a New Reminder</h2>
<form action="/reminders" method="post">
<label for="reminder-title">Title:</label>
<input id="reminder-title" type="text" name="title">
<label for="reminder-body">Body:</label>
<input id="reminder-body" type="text" name="body">
<input type="submit">
</form>
Finally our updated remindersController
will be:
var Reminder = require("../models/reminder")
var remindersController = {
index: function(req, res) {
Reminder.find({}, function(err, docs) {
res.render("reminders/index", {reminders: docs});
});
},
new: function(req, res) {
res.render("reminders/new")
},
create: function(req, res) {
// strong params
var title = req.body.title;
var body = req.body.body;
Reminder.create({title: title, body: body}, function(err, doc) {
// if there there is an error: redirect to reminders#new; else: redirect to reminders#index
err ? res.redirect("/reminders/new") : res.redirect("/reminders");
})
}
}
module.exports = remindersController;
- First, verify that a user can see reminders on
localhost:3000/reminders
. Also, check that a user can make a new reminder by going tolocalhost:3000/reminders/new
.
- A user should be able to click on any reminder's title on the
reminders
page and be directed to the specificreminder
page that displays information about that reminder. Add that functionality to your reminders page. Bonus: add more information to this page that only shows up on this page, but not on the index page with all reminders.
- Checkout the
solution-code
directory and examine the filedb/console.js
. It drops you into a REPL session with the database and all your models pre-loaded. You can runnode db/console.js
to see for yourself. CRUD some data in this REPL.
Note: If for some reason it's not working, try restarting your
mongod
process.
- How would you refactor your
index.js
so that all the configuration logic lived in a fileconfig/config.js
? - How about refactoring the code so that your routes live in
config/routes.js
?
All content is licensed under a CCBYNCSA 4.0 license. All software code is licensed under GNU GPLv3. For commercial use or alternative licensing, please contact legal@ga.co.