×
By the end of this chapter, you should be able to:
Now that we have created a schema and a corresponding model, let's see what kinds of methods exist on our model to perform CRUD operations! Remember, CRUD stands for Create, Read, Update, and Delete.
In mongoose, documents are instances of the model. There are two ways of creating documents, either by creating an object from the model using the new
keyword, or through using the built in create
method.
// invoke the constructor const elie = new Instructor({ firstName: "Elie" }); // call .save on the object created from the constructor (which is the model) elie .save() .then(newElie => { console.log(newElie); }) .catch(err => { console.log("Error saving!", err); }); // OR // invoke the create method directly on the model Instructor.create({ firstName: "Elie" }) .then(newInst => { console.log(newInst); }) .catch(err => { console.log("Error creating!"); });
There are quite a few ways to query for information in mongoose.
// finding multiple records Instructor.find({}) .then(instructors => { console.log(instructors); }) .catch(err => { console.log("error!", err); }); // finding a single record Instructor.findOne({ firstName: "Elie" }) .then(inst => { console.log(inst); }) .catch(err => { console.log("error!", err); }); // finding by id - this is very useful with req.params! Instructor.findById(2) .then(inst => { console.log(inst); }) .catch(err => { console.log("error!", err); }); // Finding in a nested object, using "query builder syntax" const query = Person.findOne({ "name.first": "Elie" }); // selecting the `name` and `occupation` fields query.select("name occupation"); // execute the query at a later time query .exec() .then(person => { console.log(person); }) .catch(err => { console.log("ERROR!"); });
You can read more about querying here
There are quite a few ways to update with mongoose as well. Here are some examples:
// update multiple records Instructor.update({}, { isHilarious: false }) .then(insts => { console.log(insts); }) .catch(err => { console.log("error!", err); }); // update a single record Instructor.findOneAndUpdate({ firstName: "Elie" }, { firstName: "Bob" }) .then(inst => { console.log(inst); }) .catch(err => { console.log("error!", err); }); // update a single record and find by id (very useful with req.params!) Instructor.findByIdAndUpdate(1, { firstName: "Bob" }) .then(inst => { console.log(inst); }) .catch(err => { console.log("error!", err); });
You can read more about each of these methods here:
http://mongoosejs.com/docs/api.html#model_Model.update
http://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
http://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate
As you might have guessed, there are also quite a few ways as well to remove a document with mongoose. Here are some examples:
// remove multiple records Instructor.remove({ isHilarious: false }) .then(insts => { console.log(insts); }) .catch(err => { console.log("error!", err); }); // find and remove Instructor.findOneAndRemove({ firstName: "Elie" }) .then(inst => { console.log(inst); }) .catch(err => { console.log("error!", err); }); // find by id and remove (very useful with req.params) Instructor.findByIdAndRemove(1) .then(inst => { console.log(inst); }) .catch(err => { console.log("error!", err); });
You can read more here about findOneAndRemove and findByIdAndRemove.
Now that we have seen how to use mongoose, let's add this logic to a CRUD application. Let's also think about a better folder structure! Let's start with our application in the terminal. We're going to build a simple CRUD application with pets
as our resource. Let's start with our folder structure in terminal.
mkdir pets-app && cd pets-app touch app.js npm init -y npm install express pug body-parser method-override mongoose mkdir views routes models touch views/{base,index,new,edit,show}.pug touch models/{index,pet}.js touch routes/pets.js
Let's start with our models/pet.js
:
const mongoose = require("mongoose"); const petSchema = new mongoose.Schema({ name: String }); const Pet = mongoose.model("Pet", petSchema); module.exports = Pet;
Next, our models/index.js
:
const mongoose = require("mongoose"); /* Database config, this can be placed in another file if you want just make sure it runs when your server starts */ mongoose.set("debug", true); mongoose.Promise = Promise; mongoose .connect( "mongodb://localhost/pets-app", { useMongoClient: true // this option is necessary for Mongoose 4.11 and up } ) .then(() => { // once connected, give a success message console.log("Connected to MongoDB"); }) .catch(err => { // if something goes wrong let us know console.log(err); }); // exporting models from other files in our module.exports object exports.Pet = require("./pet");
Now let's move to our routes/pets.js
:
const express = require("express"); const { Pet } = require("../models"); const router = express.Router(); // all pets at /pets router .route("") // this is equivalent to /pets .get((req, res, next) => { return Pet.find().then(pets => { return res.render("index", { pets }); }); }) .post((req, res, next) => { return Pet.create(req.body).then(pet => { return res.redirect("/"); }); }); // create a new pet form router.route("/new").get((req, res, next) => { return res.render("new"); }); // pets by ID routes /pets/:id router .route("/:id") .get((req, res, next) => { return Pet.findById(req.params.id).then(pet => { return res.render("show", { pet }); }); }) .patch((req, res, next) => { return Pet.findByIdAndUpdate(req.params.id, req.body).then(pet => { return res.redirect("/"); }); }) .delete((req, res, next) => { return Pet.findByIdAndRemove(req.params.id).then(pet => { return res.redirect("/"); }); }); // edit or delete a pet form /pets/:id/edit router.route("/:id/edit").get((req, res, next) => { return Pet.findById(req.params.id).then(pet => { return res.render("edit", { pet }); }); }); module.exports = router;
Then in our routes/index.js
:
exports.petRouter = require("./pets");
Before we move onto our views, let's finish our app.js
:
// npm packages const bodyParser = require("body-parser"); const express = require("express"); const methodOverride = require("method-override"); const morgan = require("morgan"); // app imports const { petRouter } = require("../router"); // ES6 object destructuring from the obj // globals const app = express(); app.set("view engine", "pug"); // middleware app.use(morgan("tiny")); app.use(bodyParser.urlencoded({ extended: true })); app.use(methodOverride("_method")); // route handlers app.use("/pets", petRouter); app.get("/", (req, res, next) => { return res.redirect("/pets"); }); // catch 404 and forward to error handler app.use((req, res, next) => { const err = new Error("Not Found"); err.status = 404; return next(err); }); /* error handler - for a handler with four parameters, the first is assumed to be an error passed by another handler's "next" */ app.use((err, req, res, next) => { res.status(err.status || 500); return res.render("error", { message: err.message, /* if we're in development mode, include stack trace (full error object) otherwise, it's an empty object so the user doesn't see all of that */ error: app.get("env") === "development" ? err : {} }); }); // server start app.listen(3000, () => { console.log("Server is listening on port 3000"); });
Now let's move onto our views. First, here's views/base.pug
:
<!DOCTYPE html> html(lang="en") head meta(charset="UTF-8") title Document body block content
Now our views/index.pug
:
extends base.pug block content a(href="/pets/new") Create a new pet each pet in pets p `Name ${pet.name}` | a(href=`/pets/${pet.id}/edit`) Edit
Now our views/new.pug
extends base.pug block content h1 Add a new pet! form(action="/pets", method="POST") input(type="text", name="name") input(type="submit", value="Add a Pet!")
Now our views/show.pug
extends base.pug block content h1 Welcome to `${pet.name}'s` show page!
Finally, our views/edit.pug
extends base.pug block content h1 Edit a pet! form(action=`/pets/${pet.id}?_method=PATCH`, method="POST") input(type="text", name="name", value=`${pet.name}`) input(type="submit", value="Edit a Pet!") form(action=`/pets/${pet.id}?_method=DELETE`, method="POST") input(type="submit", value="X")
As you can see above, building these applications is a process that requires repetition to understand and become comfortable with. Take the time to build a few of these applications and see and debug errors as you go along!
You can see another sample CRUD app with Mongoose here.
Watch this screencast to solidy your understanding of how to implement CRUD with Mongoose:
When you're ready, move on to Mongoose Associations