✒️
SEI 802
  • Introduction
  • About These Notes
  • Syllabus
  • Development Workflow
    • Installfest
      • Mac OSX
      • Linux
      • Git Configuration
      • Sublime Packages
    • Command Line
      • The Terminal
      • Filesystem Navigation
      • File Manipulation
      • Additional Topics
    • Intro to Git
      • Version Control
      • Local Git
      • Remote Git
      • Git Recipes
    • Group Collaboration
      • Git Workflows
      • Project Roles and Tools
    • VS Code Tips & Tricks
  • HTML/CSS
    • HTML
    • CSS Selectors
    • CSS Box Model and Positioning
      • Box Model
      • Display and Positioning
      • Flexbox
      • Grid
      • Flexbox & Grid Games
      • Floats and Clears
      • Additional Topics
    • Advanced CSS
      • Responsive Design
      • Pseudo-Classes/Elements
      • Vendor Prefixes
      • Custom Properties
      • Additional Topics
    • Bootstrap
    • CSS Frameworks
    • Accessibility
  • JavaScript
    • Primitives
    • Arrays
    • Objects
    • Control Flow
      • Boolean Expressions
      • Conditionals
      • Loops
      • Promises
    • Functions
      • Callbacks
      • Timing Functions
      • Iterators
    • DOM and Events
    • DOM Manipulation
    • HTML5 Canvas
    • How To Reduce Redundancy
    • (2019) JavaScript OOP
    • (2016) OOP with Classes
    • (1995) OOP with Prototypes
      • Constructors
      • Prototypes
    • Intro to TDD
    • Scoping
    • Inheritance
      • Prototypal Inheritance
      • Call, Apply, and other Functions
      • ES6 Inheritance
      • Resources
    • Custom Node Modules
    • Additional Topics
      • AJAX, Fetch, and Async/Await
      • AJAX w/JSON and Localstorage
        • AJAX w/JSON
        • Local Storage
      • Async module
      • Data Scraping
  • jQuery
    • Intro
      • DOM Manipulation
      • Reddit Practice
      • Styling
      • Events
    • Plugins
    • AJAX
  • APIs
    • Fetch
    • AJAX w/jQuery
    • AJAX w/Fetch
  • Databases
    • Intro to SQL
    • Advanced SQL
    • MongoDB
      • Intro to NoSQL
      • CRUD in MongoDB
      • Data Modeling
      • Intermediate Mongo
  • Node/Express
    • Node
      • Intro to Node
      • Node Modules
      • Node Package Manager (NPM)
    • Express
      • Intro to Express
        • Routes
        • Views
        • Templates
        • Layouts and Controllers
        • CRUD & REST
          • Get and Post
          • Put and Delete
      • APIs with Express (request)
      • APIs with Express (axios)
    • Sequelize
      • Terminology
      • Setup
      • Using Models
      • Seeding Data
      • Validations and Migrations
      • Resources
      • 1:M Relationships
      • N:M Relationships
    • Express Authentication
      • Research Components
      • Code Components
      • Auth in Theory
        • Sessions
        • Passwords
        • Middleware
        • Hooks
      • Auth in Practice
        • Create the User
        • User Signup
        • Sessions
        • User Login
        • Authorization and Flash messages
    • Testing with Mocha and Chai
    • Mongoose
      • Mongoose Associations
    • JSON Web Tokens
      • Codealong
    • Additional Topics
      • oAuth
      • Geocoding with Mapbox
      • Geocoding and Google Maps
      • Cloudinary
      • Websockets with Socket.io
      • SASS
  • Ruby
    • Intro to Ruby
    • Ruby Exercises
    • Ruby Classes
    • Ruby Testing with Rspec
    • Ruby Inheritance
    • Ruby Data Scraping
  • Ruby on Rails
    • Intro to Rails
    • APIs with Rails
    • Asset Pipeline
    • Rails Auth and 1-M
      • Auth Components
    • Rails N:M
    • ActiveRecord Polymorphism
    • Additional Topics
      • oAuth
      • SASS
      • Rails Mailers
      • Cloudinary
      • Jekyll
  • React (Updated 2019)
    • ES6+/ESNext
      • Const and Let
      • Arrow Functions
      • Object Literals and String Interpolation
      • ES6 Recap
      • ES6 Activity
    • Intro to React
      • Create React App
      • Components and JSX
      • Virtual DOM
      • Props
      • Dino Blog Activity
      • Nested Components
      • Lab: LotR
    • React State
      • Code-Along: Mood Points
      • Code-Along: Edit Dino Blog
      • Lab: Simple Calc
      • Lifting State
    • React Router
      • Browser History/SPAs
      • React Router (lesson and full codealong)
      • Router Lab
    • Fetch and APIs
      • APIs with Fetch and Axios
      • Fetch the Weather
    • React Hooks
    • React LifeCycle
      • Lab: Component LifeCycle
    • React Deployment
    • Additional Topics
      • React Frameworks
        • Material UI Theming
      • Typescript
        • More Types and Syntax
        • Tsconfig and Declaration Files
        • Generics with Linked List
      • Redux
      • TypeScript
      • Context API
      • React Native
  • Meteor
  • Deployment and Config
    • Deploy - Github Pages
    • Deploy - Node/Sequelize
    • Deploy - Node/MongoDB
    • Deploy React
    • Deploy - Rails
      • Foreman (Environment Variables)
    • Deploy - AWS Elastic Beanstalk
    • Deploy - S3 Static Sites
    • Deploy - Django
    • Deploy - Flask
  • Data Structures and Algorithms
    • Recursion
    • Problem Solving - Array Flatten
    • Binary Search
    • Algorithm Complexity
    • Stacks and Queues
    • Bracket Matching
    • Ruby Linked Lists
      • Sample Code
      • Beginner Exercises
      • Advanced Exercises
    • JS Linked Lists
      • Sample Code
      • Beginner Exercises
      • Beginner Solutions
    • Hash Tables
    • Intro to Sorting
    • Insertion Sort
    • Bucket Sort
    • Bubble Sort
    • Merge Sort
    • Quick Sort
    • Heap Sort
    • Sorting Wrapup
    • Hashmaps
    • Trees and Other Topics
  • Python
    • Python Installation
    • Intro to Python
    • Python Lists
    • Python Loops
    • Python Dictionaries
    • Python Sets and Tuples
    • Python Cheatsheet
    • Python Functions
    • Python Classes
    • Python Class Inheritance
    • Intro to Flask
    • Intro to SQLAlchemy
      • Flask and SQLAlchemy
    • Using PyMongo
    • Intro to Django
    • CatCollector CodeAlong
      • URLs, Views, Templates
      • Models, Migrations
      • Model Form CRUD
      • One-to-Many Relations
      • Many-to-Many Relations
      • Django Auth
    • Django Cheatsheet
    • Django Auth
    • Django Polls App Tutorial
    • Django School Tool Tutorial
    • Django 1:M Relationships
    • Custom Admin Views
    • Data Structures and Algorithms
      • Recursion
      • Binary Search
      • Stacks and Queues
      • Linked Lists
      • Binary Trees
      • Bubble Sort
      • TensorFlow & Neural Networks
    • Adjacent Topics
      • Raspberry Pi
      • Scripting
  • Assorted Topics
    • History of Computer Science
    • Regular Expressions
    • Intro to WDI (Course Info)
    • Being Successful in WDI
    • Internet Fundamentals
      • Internet Lab
    • User Stories and Wireframing
      • Wireframing Exercise: Build an Idea
    • Post WDI
      • Learning Resources
      • Deliverables -> Portfolio
      • FAQ
  • Projects
    • Project 1
    • Project 2
    • Project 3
      • Project 3 Pitch Guidelines
    • Project 4
    • Past Projects
      • Project 1
      • Project 2
      • Project 3
      • Project 4
      • Portfolios
    • Post Project 2
    • MEAN Hackathon
      • Part 1: APIs
      • Part 2: Angular
    • Portfolio
  • Web Development Trends
  • Resources
    • APIs and Data
    • Tech Websites
    • PostgreSQL Cheat Sheet
    • Sequelize Cheat Sheet
    • Database Administration
  • Archived Section
    • (Archived) ReactJS
      • Intro to React
        • Todo List Codealong
        • Additional Topics
      • Deploy React
      • React with Gulp and Browserify
        • Setting up Gulp
        • Additional Gulp Tasks
      • React Router
        • OMDB Router
        • OMDB Search
        • Additional Resources
      • React Animations
        • CSS Animations
    • AngularJS
      • Intro to AngularJS
        • Components and SPA
        • Create an Angular App
      • Angular Directives and Filters
      • Angular Animation
      • Angular Bootstrap Directives
        • Bootstrap Modals
      • Angular $http
      • Angular Services
        • Service Recipes
        • ngResource
        • Star Wars Codealong
      • Angular Routing
      • Angular + Express
      • Angular Authentication
        • Additional Topics
      • Angular Components
      • Angular Custom Filters
      • Angular Custom Directives
Powered by GitBook
On this page
  • Objectives
  • MongoDB + Mongoose
  • The Big Picture
  • Big Picture Example
  • Setting up Mongoose in your app
  • Mongoose Event Handlers
  • Working with Models
  • Interacting with MongoDB's CRUD
  • Independent Practice

Was this helpful?

  1. Node/Express

Mongoose

Objectives

  • Update & destroy a model

  • Initialize & create a new instance of a model

  • Perform basic find queries

  • Reference other documents in an instance of a model

  • Work with embedded and referenced documents with Mongoose

MongoDB + Mongoose

Mongoose is to MongoDB as Sequelize is to a SQL database. But, because it maps code to MongoDB documents, it is referred to as an Object Document Mapper (ODM) instead of an ORM. Mongoose is going to make it easier to perform CRUD using object-oriented JS instead of working directly MongoDB.

Using the Mongoose ODM is by far the most popular way to perform CRUD on a MongoDB. Mongoose's homepage says it best:

"Mongoose provides a straight-forward, schema-based solution to model your application data"

Wait a second, what's with this "schema" business, isn't MongoDB schema-less? Well, yes it is, however, the vast majority of applications benefit when their data conforms to a defined structure (schema). Mongoose allows us to define schemas and ensures that documents conform.

Mongoose also provides lots of other useful functionality:

  • Default property values

  • Validation

  • Automatic related model population via the populate method

  • Virtual properties - create properties like "fullName" that are not persisted in the database

  • Custom Instance methods which operate on the document

  • Static methods which operate on the entire collection

  • pre and post event lifecycle hooks (Mongoose "middleware")

The Big Picture

Here is the big picture overview of the components we'll be working with:

Big Picture Example

This is the general flow of how we will use Mongoose...

Make a Schema

What is a schema? It is just the structure of our data model: essentially the field names and what data type they are. This is similar the the schema we could see of each of our data tables in postgres. Viewing the table schema showed each of the columns and their data types. Recall that colmuns in SQL translate into fields or attributes in MongoDB.

We define the structure of our model as a schema...

const postSchema = new mongoose.Schema({
  content: String
});

This will make a single field named content and it will be a string.

Then we use this schema to build an actual model class...

module.exports = mongoose.model('Post', postSchema);

This creates a model called Post (REMEMBER MODEL NAMES ARE ALWAYS SINGULAR). This two-step process results in a usable model similar to the ones we saw in Sequelize. It can be required and used to perform CRUD on the posts collection in the MongoDB:

const Post = require('./models/post');
Post.create({content: 'Amazing post...'});

Setting up Mongoose in your app

Create a new Express app and install the relevant npm packages:

mkdir family-tree
cd family-tree

# setup npm
npm init -y

# install dependencies
npm install express 

# create index file
touch index.js

To use Mongoose in your Node app:

npm install mongoose

With the package installed, lets use it - open index.js and setup your app:

const express = require('express');
const app = express();

app.use(express.urlencoded({ extended: false }));

// Mongoose stuff
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/familyTree');

app.get('/', function(req, res) {
  res.send('Hi!');
});

app.listen(3000);

You can now execute all the mongoDB commands over the database familyTree, which will be created if it doesn't exist.

Mongoose Event Handlers

Let's modify our index.js as follows:

    const mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/familyTree');

    // shortcut to mongoose.connection object
    const db = mongoose.connection;

    db.once('open', function() {
          console.log(`Connected to MongoDB at ${db.host}:${db.port}`);
    });

    db.on('error', function(err) {
          console.error(`Database error:\n${err}`);
    });

We set up an event listener to fire once when the connection 'opens' to console log what host and port it is running on. It will also console log any errors whenever they occur.

Working with Models

Defining a Model

Like the ORMs we've worked with previously, Mongoose allows us to define models, complete with attributes, validations, and middleware (known as hooks in Sequelize). Let's make a model.

From within our family-tree app:

mkdir models
touch models/user.js

Now let's add:

const mongoose = require('mongoose');

// create a schema
const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, required: true, unique: true },
  age: Number,
  website: String
});

MongoDB is schemaless, meaning: all the documents in a collection can have different fields, but for the purpose of a web app, often containing validations, we can still use a schema will cast and validate each type. Also note that we can have nested structures in a Mongoose model.

At the moment we only have the schema, representing the structure of the data we want to use. To save some data, we will need to make this file a Mongoose model and export it:

//in users.js
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, required: true, unique: true },
  meta: {
    age: Number,
    website: String
  }
});

// Here is where you actually name the model. NAME IT SINGULAR!
const User = mongoose.model('User', userSchema);

// make this available to our other files
module.exports = User;

Notice that you can use objects and nested attributes inside an object.

Here's a look at the datatypes we can use in Mongoose documents:

  • String

  • Number

  • Date

  • Boolean

  • Array

  • Buffer (binary)

  • Mixed (anything)

  • ObjectId

Also, notice we create the Mongoose Model with mongoose.model. Remember, we can define custom methods here - this would be where we could write a method to encrypt a password.

Timestamps in Mongoose

Mongoose will add createdAt and add/update updatedAt fields if we set the timestamps option as follows in the schema:

const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, required: true, unique: true },
  meta: {
    age: Number,
    website: String
  }
}, {
    timestamps: true
});

Creating Custom Methods

When defining a schema, you can add custom methods and call these methods on the models. These are instance methods. Let's write a sayHello function under our schema:

const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, required: true, unique: true },
  meta: {
    age: Number,
    website: String
  }
});

userSchema.methods.sayHello = function() {
  return "Hi " + this.name;
};

const User = mongoose.model('User', userSchema);

module.exports = User;

Now we can call it by requiring the User model in index.js:

const User = require('./models/user');

// create a new user called Chris
const chris = new User({
  name: 'Chris',
  email: 'chris@gmail.com',
  meta: {
    age: 27,
    website: 'http://chris.me'
  }
});

app.get('/', function(req, res) {
  res.send(chris.sayHello());
});

Now run the app with nodemon to see the result! You can define class methods in a similar manner by attaching the method to .statics instead of .methods

Interacting with MongoDB's CRUD

Let's hope into an interactive shell and test out CRUD functionality. To do this, from our app directory, we'll have to type in node and then require our Models manually.

Create

We can create a User using the .save method in Mongoose. You can also call .create to combine creating and saving the instance.

const newUser = User({
  name: 'bob',
  email: 'bob@gmail.com'
});

// save the user
newUser.save(function(err) {
  if (err) return console.log(err);
  console.log('User created!');
});

// create and save a user
User.create({ name: 'Emily', email: 'em@i.ly' }, function(err, user) {
  if (err) return console.log(err);
  console.log(user);
});

There is no "find or create" in Mongoose.

What about Read?

We can find multiple model instances by using the .find function, which accepts an object of conditions. There's also .findOne and .findById available.

// Find All
User.find({}, function(err, users) {
  if (err) return res.send(err);
  res.send(users);
});

// Find only one user
User.findOne({}, function(err, user) {
  if (err) return res.send(err);
  res.send(user);
});

// Find by email
User.find({ email: req.params.email }, function(err, user) {
  if (err) return res.send(err);
  res.send(user);
});

// Find by id
User.findById(req.params.id, function(err, user) {
  if (err) return res.send(err);
  res.send(user);
});

Update

Models can be updated in a few different ways - using .update(), .findByIdAndUpdate(), or .findOneAndUpdate():

// updates all matching documents
User.update({ name: 'brian' }, { meta: { age: 26 } }, function(err, user) {
  if (err) console.log(err);
  console.log(user);
});

// updates one match only
User.findOneAndUpdate({ name: 'brian' }, { meta: { age: 26 } }, function(err, user) {
  if (err) console.log(err);
  console.log(user);
});

Destroy

Models can be removed in a few different ways - using .remove(), findByIdAndRemove(), and .findOneAndRemove().

// find all users with the name Brian and remove them
User.remove({ name: 'brian' }, function(err) {
  if (err) console.log(err);
  console.log('Users deleted!');
});

// find the user with id 4 and remove it
User.findOneAndRemove({ name: 'brian' }, function(err) {
  if (err) console.log(err);
  console.log('User deleted!');
});

Independent Practice

  • GET users/, this will return all the documents

  • POST users/, given some arguments in the url, this method will create a user record.

  • DELETE users/:id, will remove the document corresponding to the collection

PreviousTesting with Mocha and ChaiNextMongoose Associations

Last updated 3 years ago

Was this helpful?

Note that in the .find function, you can also use MongoDB queries such as $gt, $lt, $in, and others. Alternatively, there's a new .where syntax that can be used as well.

Using the code we just wrote and the , add three routes to your Express app.

Documentation on Model.where can be found here
official Mongoose Models docs