Intro To Mongoose

Learning Objectives
Students Will Be Able To: |
---|
Describe the Use Case for Mongoose |
Define a Basic Schema for a Model |
Connect to a MongoDB Using Mongoose |
Create Documents Using a Model |
Read Documents Using a Model |
Define Default Values in a Schema |
Define Validations in a Schema |
Road Map
- Demo of the
mongoose-movies
Reference Application - Setup
- Intro to Mongoose
- Using a
.env
File to Protect "Secrets" - Including Mongoose in a Project
- Defining Schemas in Mongoose
- Built-in Types for Properties
- Compiling Schemas into Models
- Use a Model to Create Data in a Node REPL
- Review the 5-Step Process to Add a Feature to a Web App
- Implement the
new
Functionality - Implement the
create
Functionality - Use a Model to Read Data
- Implement
index
Functionality - Further Study
- Defining Default Values for a Property
- Defining Validations for a Property
1. Demo of the mongoose-movies
Reference Application
As we continue to learn new development concepts and techniques during this unit we'll be incorporating them in a reference app named Mongoose Movies.
In fact, we'll be building a reference app for each full-stack unit that can be referred to when coding your labs and project.
2. Setup
We'll be learning about Mongoose over several lessons so a code-along repo has been set up to get started and to sync with if need be.
To get set up:
-
Move into your code folder:
cd ~/code
SKIP cloning the repo
-
Clone the repo containing the starter code:
git clone https://git.generalassemb.ly/sei-blended-learning/mongoose-movies.git
-
Create a new express-generator app
npx express-generator --no-view mongoose-movies
-
Move into the newly created project mongoose-movies folder:
cd mongoose-movies
-
Install the Node modules:
npm i
-
Open the project in VS Code:
code .
-
Change
app.js
toserver.js
for best practicesmv app.js server.js
Update the imports in
bin/www
var app = require('../server');
-
Open an integrated terminal in VS Code (
control + backtick
) and spin up the server:nodemon
SKIP browsing to localhost
Browsing to localhost:3000
we find that the starter code is a skeleton Express server created using express-generator and updated as follows:
- As a best practice,
app.js
has been renamed toserver.js
- Partial templates (
header.ejs
&footer.ejs
) have been set up

3. Intro to Mongoose
What is Mongoose?
Yes, this guy, but not in the context of MongoDB...

Mongoose is software library called an Object Document Mapper (ODM) because it maps object-oriented JavaScript to MongoDB documents.
Why use Mongoose?
Mongoose is the goto when performing CRUD data operations on a MongoDB database.
Using Mongoose is easier and more productive than working directly MongoDB.
As stated on Mongoose's homepage:
"Mongoose provides a straight-forward, schema-based solution to model your application data..."_
Wait a minute, what's with this "schema" business, isn't MongoDB schema-less?
Well, yes MongoDB is, however, it turns out that the vast majority of applications benefit when their data conforms to a defined structure (schema).
In addition to ensuring that data conforms to schemas that we define Mongoose also provides lots of other useful functionality such as:
- Default property values
- Data 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 a document
- Custom Static methods which operate on the entire collection
pre
andpost
event lifecycle hooks (Mongoose "middleware")
The Big Picture
Here is a big picture overview of the purpose of Mongoose's Schema and Model components:
Big Picture Example
Assume we need to store cat documents with the following schema:
const catSchema = new mongoose.Schema({
name: String,
breed: String
});
catSchema
can then be compiled into a Model and that Model exported like this:
// By convention, the name of the Model is singular and UpperCamelCased
module.exports = mongoose.model('Cat', catSchema);
Then, in our controller actions, we can require the Cat
model and use it to perform CRUD on the cats
collection in the MongoDB:
// Again, convention is to name the Model's
// variable as singular and UpperCamelCased
const Cat = require('./models/cat');
// SKIP old code
// Cat.create({name: 'Morris', breed: 'Orange Tabby'}, function(err, catDoc) {
// console.log(catDoc);
// });
const kitty = new Cat({name: 'Morris', breed: 'Orange Tabby'});
kitty.save().then(() => console.log('meow'));
❓ Review Questions
(1) Describe the use case for Mongoose, i.e., what is it and why choose to use it?
Mongoose is a JavaScript library known as an Object Document Mapper (ODM) and we'll want to use it any time a project is CRUDing a MongoDB database.
(2) A Mongoose _________ is compiled into a Mongoose Model.
Schema
(3) We use a Mongoose _________ to perform CRUD operations on a particular MongoDB collection.
Model
4. Using a .env
File to Protect "Secrets"
A .env
file is used to provide environment variables (also called config vars).
Environment variables include the database's connection string and other secrets such as access tokens, etc.
Because they usually hold sensitive info, .env
files should never be pushed to GitHub and thus need to be Git ignored.
Environment variables can also be used to configure an application's settings without changing the app's source code.
Setting Up the .env
First, create a .env
file in the project root:
touch .env
Install the dotenv
Node module:
npm i dotenv
In server.js, require dotenv
and call its config()
method to "process" the KEY=VALUE
pairs in the .env
:
const logger = require('morgan');
// It's very important to require dotenv before any other module
// that depends upon the properties added to process.env
require('dotenv').config();
Note that we're not assigning the returned value to a variable - just simply coding require('dotenv').config()
results in the .env
file being "processed".
Adding the Atlas MongoDB Connection String to the .env
Find and copy your Atlas MongoDB connection string that you previously created and tucked away for safe keeping.
In the .env
file, add a key of DATABASE_URL
followed by an equals sign (=
) then paste in your connection string with no spaces resulting in something like this:
DATABASE_URL=mongodb+srv://someuser:somepassword@cluster0-oxpsb.azure.mongodb.net/?retryWrites=true&w=majority
Note that every KEY=VALUE
pair defined in .env
will be available as a property on Node's process.env
object, e.g., process.env.DATABASE_URL
.
Next we need to insert the name of the database we want to create and connect to, mongoose-movies
will work just fine for this project. Insert it between the /?
characters like this:
DATABASE_URL=mongodb+srv://someuser:somepassword@cluster0-oxpsb.azure.mongodb.net/mongoose-movies?retryWrites=true&w=majority
👀 This might be a good time to update your saved connection string in its new form for future use. Just be sure to change the database name, e.g., change
/mongoose-movies?
to to/another-db-name?
in your next project.
FYI, nodemon
doesn't watch for changes in the .env
file, so you may have to restart the server manually if you're not going to make some additional code changes.
5. Including Mongoose in a Project
To use Mongoose in our apps, we need to install and configure it...
Install Mongoose
Installing the Mongoose module is straight forward:
npm i mongoose
Reminder:
npm i
is a shortcut fornpm install
Connect to MongoDB using Mongoose in a module
We're going to create a separate module named database.js
and put it in a folder named config
:
mkdir config
touch config/database.js
Then in database.js
, let's connect to a database named movies
:
const mongoose = require('mongoose');
mongoose.set("debug", true);
mongoose.connect(process.env.DATABASE_URL);
As you know, the code in a Node module doesn't execute until the module is required, therefore, to connect to the database, we'll require database.js
in server.js
:
const logger = require('morgan');
require('dotenv').config();
// connect to the database with AFTER the config vars are processed
require('./config/database');
Again, there's no need to assign to a variable because we're not exporting anything useful.
Verify the Connection
Errors in the Express terminal - look closely, it's probably a failure to authenticate with Alas error - please double-check your connection string and its credentials.
No errors in the Express terminal? Great! However, wouldn't it be nice to know that our connection to our database was successful? Sure it would...
Adding event listeners to the Mongoose connection
The Mongoose connection object inherits from Node's EventEmitter
which allows us to listen to defined events.
Let's listen to the connected
event by modifying the database.js
module as follows:
const mongoose = require('mongoose');
mongoose.set("debug", true);
mongoose.connect(process.env.DATABASE_URL);
// shortcut to mongoose.connection object
const db = mongoose.connection;
db.on('connected', function() {
console.log(`Connected to MongoDB ${db.name} at ${db.host}:${db.port}`);
});
After saving, nodemon
will restart the server and you should see the Connected to MongoDb... message in the Express terminal.
👀 Do you need to sync your code?
git reset --hard origin/sync-1-connect-database
6. Defining Schemas in Mongoose
Next, we're going to:
- Create a module for the Schema/Model
- Define a basic Schema for a
Movie
model
Create a module for the Schema/Model
So, where are we going to put our app's schemas and models?
The MVC design pattern is all about code organization and it makes sense to organize modules that define schemas/models within a models
folder:
mkdir models
touch models/movie.js
There will always be a module per Mongoose Model where:
- We define the schema,
- Compile the schema into a model, and
- Export that model.
Define a basic Schema for a Movie
model
In the schema/model module, we will start with this code:
const mongoose = require('mongoose');
// optional shortcut to the mongoose.Schema class
const Schema = mongoose.Schema;
Creating the shortcut to the mongoose.Schema
class is optional but convenient when defining most schemas.
Now let's define the basic schema for the Movie
Model:
const Schema = mongoose.Schema;
const movieSchema = new Schema({
title: String,
releaseYear: Number,
mpaaRating: String,
cast: [String]
});
Note the cast
property's type is an Array of Strings.
Mongoose vocabulary: A property may also be referred to as a "path".
👉 YOU DO - Define Another Schema Property (1 min)
- Add an additional property named
nowShowing
with a type ofBoolean
(make sure that it's upper-cased so that it refers to JavaScript's built-inBoolean
object wrapper).
Try not to peek...
const movieSchema = new Schema({
title: String,
releaseYear: Number,
mpaaRating: String,
cast: [String],
// Newly added property below
nowShowing: Boolean
});
Add the timestamps
Option
Mongoose can automatically create createdAt
and updatedAt
fields to every document if we provide a second argument to the Schema
constructor and set a timestamps
property in it as follows:
const movieSchema = new mongoose.Schema({
...
}, {
timestamps: true
});
This really comes in handy so it's recommended to pretty much add the timestamps: true
option to all schemas that you define.
Awesome! We've defined a Mongoose schema!
As we progress toward learning more about Mongoose, we will be adding additional functionality to the movieSchema
.
7. Built-in Types for Properties
Here are the eight built-in schemaTypes
available:
String
Number
Boolean
Date
mongoose.Schema.Types.ObjectId
mongoose.Schema.Types.Buffer
[]
(Array)mongoose.Schema.Types.Mixed
Of note are a few types above that are not built into JavaScript:
mongoose.Schema.Types.ObjectId
mongoose.Schema.Types.Buffer
mongoose.Schema.Types.Mixed
When we need to specify one of the above types, e.g., ObjectId
, we will need to ensure that we access them through the object hierarchy.
Defining that Schema
shortcut variable, enables us to write Schema.Types.ObjectId
, leaving off the mongoose.
.
8. Compiling Schemas into Models
Reminder: Models, not schemas are used to perform CRUD on a MongoDB collection.
Compiling a schema into a model is as easy as calling the mongoose.model
method:
const Schema = mongoose.Schema;
const movieSchema = new Schema({
title: String,
releaseYear: Number,
mpaaRating: String,
cast: [String],
nowShowing: Boolean
}, {
timestamps: true
});
// Compile the schema into a model and export it
module.exports = mongoose.model('Movie', movieSchema);
Again, there is a one-to-one mapping between Mongoose models and MongoDB collections.
By default, the collection in the database will be named as the pluralized version of the model in all lower-case.
Congrats on defining your first Mongoose schema and exporting its model!
👀 Do you need to sync your code?
git reset --hard origin/sync-2-create-model
9. Use a Model to Create Data in a Node REPL
Now that we have a model, we're ready to perform some CRUD.
First up is creating data!
We can use a Mongoose Model in two ways to create documents in the collection:
-
const instance = new Model(<object>)
, theninstance.save()
-OR- -
Model.create(<object or array of objects>)
Creating documents in a Node REPL
Before writing the code in the app to create movies, let's do so in a Node REPL...
Warning, if you make a typo, you'll have to start over:
node
> require('dotenv').config() // Necessary if connection string in a .env file
> require('./config/database')
> const Movie = require('./models/movie')
> Movie.create({
... title: 'Star Wars - A New Hope',
... releaseYear: 1977
... }).then(console.log)
Pretty neat how we can provide the
console.log
method as the onFulfilled callback function.
Logged out will be a document that looks something like...
{ __v: 0,
title: 'Star Wars - A New Hope',
releaseYear: 1977,
_id: 57ea692bab09506a97e969ba,
cast: []
}
Note: The
__v
field is added by Mongoose to track versioning - ignore it.
Note that we did not provide a value for nowShowing
so it was not created as a property in the document.
However, properties of type Array
, are always initialized to empty arrays like cast
was. This makes it convenient to start pushing performers into it!
That was fun! Exit the REPL (ctrl + C
twice) and lets see how we can usenew
+ save
to create movie documents - but this time from within our app...
10. Review the 5-Step Process to Add a Feature to a Web App
Once again, here is the process we repeat over and over when adding a feature, including CRUD functionality, in a web app:
- Determine the "proper" route (HTTP Method & Endpoint). Use RESTful conventions whenever possible.
- Add the UI (link and/or form) that will trigger the HTTP request that matches the route.
- Define the route in the appropriate router module that will match the HTTP request and map it to the
<controller>.<method>
that will perform the desired functionality. - Add and code the controller action/function to perform any necessary CRUD, etc. and be sure to export it.
- In the controller, in the case of a
GET
request, respond withres.render
(optionally passing data to the view). Or, when data has been mutated (POST
,PUT
&DELETE
) use ares.redirect
. If rendering, code the view template if necessary.
SKIP implementing the new
functionality
11. Implement the new
Functionality
Creating Data is sometimes a two-request process
Remember, creating data functionality might be a two-request process:
- First request displays a form for the user to enter the data (the
new
functionality/controller action) - Second request to submit the form to the server where the data is created (the
create
functionality/controller action)
Yup, the new
& create
actions are partners!
❓ What other two controller actions are partners used to edit data?
edit
+ update
Displaying a <form>
to enter a new movie
Step 1 - Determine the "proper" route (HTTP Method & Endpoint)
Using our trusty Routing Chart, we find that to display a new.ejs
view with a form for entering movies, the proper route will be:
GET /movies/new
Step 2 - Add the UI
👉 YOU DO - Add a Navigation Hyperlink (1 min)
-
Inside of the existing
<nav>
element in views/partials/header.ejs, add a hyperlink (<a>
tag) that when clicked will result in the browser sending the HTTP request for the route we just identified above. -
The hyperlink should display New Movie like this:
👀 Note that a
|
has been inserted between the links to provide separation.
Step 3 - Define the route on the server
Express generator stubbed up a users.js
route module that we will convert and use for the movies resource.
Rename the file to movies.js
.
Due to the renaming, we'll need to make a couple of changes in server.js
...
❓ What two lines of code need to be updated?
var usersRouter = require('./routes/users');
and
app.use('/users', usersRouter);
Inside of routes/movies.js
, let's code the route that maps the HTTP request to the new
action:
const express = require('express');
const router = express.Router();
// You'll be creating this controller module next
const moviesCtrl = require('../controllers/movies');
// GET /movies/new
router.get('/new', moviesCtrl.new);
module.exports = router;
Step 4 - Add the controller action/method and be sure to export it
👉 YOU DO - Create the movies Controller (3 mins)
Create the controller module, export the new
action and stub up the newMovie
function - we've done this quite a few times already so do your best not to look below...
-
Create the
controllers/movies.js
module. -
The
new
action is just the first of several that are going to be exported from this module:
module.exports = {
new: newMovie
};
Remember, we can't name a function new
because it's a reserved word, but newMovie
works!
- Stub up the
newMovie
function:
function newMovie(req, res) {
}
Code the controller action/method
There's no CRUD to perform in this new
action, we just need to render a new.ejs:
function newMovie(req, res) {
// We'll want to be able to render an
// errorMsg if the create action fails
res.render('movies/new', { errorMsg: '' });
}
Step 5 - Create the View
Now we need the new.ejs
view.
As we've discussed, organizing views for a data resource into a dedicated folder makes sense. In Terminal:
mkdir views/movies
touch views/movies/new.ejs
Copy and paste the following awesome but ugly form, then we'll review it...
<%- include('../partials/header') %>
<h2>Enter a New Movie</h2>
<form action="/movies" method="POST">
<label>Title:
<input type="text" name="title">
</label><br />
<label>Release Year:
<input type="number" name="releaseYear">
</label><br />
<label>MPAA Rating
<select name="mpaaRating">
<option value="G">G</option>
<option value="PG">PG</option>
<option value="PG-13">PG-13</option>
<option value="R">R</option>
</select>
</label><br />
<label>Cast (separate actors with commas):
<input type="text" name="cast">
</label><br />
<label>Now Showing:
<input type="checkbox" name="nowShowing" checked>
</label><br />
<button type="submit">Add Movie</button>
</form>
<p><%= errorMsg %></p>
<%- include('../partials/footer') %>
Test the new
Functionality
Clicking the New Movie link should now display

👀 Do you need to sync your code?
git reset --hard origin/sync-3-new-functionality
12. Implement the create
Functionality
Here we go again...
Step 1 - Determine the "proper" route (HTTP Method & Endpoint)
Note that we've already set the action
& method
attributes to match the proper RESTful route to submit the form to.
We are going to POST to /movies
with the following JSON body:
{
"title": "SEI Movie",
"releaseYear": 2023,
"mpaaRating": "PG",
"nowShowing": "on",
"cast": "Simon Desmond",
}
We can send any JSON body that we want, but the above mirrors what a normal HTML form will send
Identify the HTTP Method and Endpoint
SKIP creating the UI - Use Insomnia to send the request
Step 2 - Add the UI
✅ Yep, we already have the <form>
that is going to trigger the request.
Step 3 - Define the route on the server
Define the create route in routes/movies.js:
// GET /movies/new
router.get('/new', moviesCtrl.new);
// POST /movies
router.post('/', moviesCtrl.create);
Step 4 - Add and code the controller action/method and be sure to export it
The next step is to stub up and export that create
controller action in controllers/movies.js:
module.exports = {
new: newMovie,
create
};
async function create(req, res) {
}
👀 We're going to be mostly using the modern
async/await
approach when consuming promises vs. using.then()
because, well,async/await
simply rock!
We're going to be using our Movie
model, so we need to require it at the top:
const Movie = require('../models/movie');
Now let's write the code that will use the Movie
Model to create the movie submitted by the form - we'll review it as we type it...
async function create(req, res) {
// convert nowShowing's checkbox of nothing or "on" to boolean
req.body.nowShowing = !!req.body.nowShowing;
// remove any whitespace at start and end of cast
req.body.cast = req.body.cast.trim();
// split cast into an array if it's not an empty string - using a regular expression as a separator
if (req.body.cast) req.body.cast = req.body.cast.split(/\s*,\s*/);
try {
const movie = await Movie.create(req.body);
// Always redirect after CUDing data
// We'll refactor to redirect to the movies index after we implement it
// res.redirect('/movies/new'); SKIP old code
res.status(201).json(movie)
} catch (err) {
// Typically some sort of validation error
console.log(err);
// res.render('movies/new', { errorMsg: err.message }); SKIP old code
res.status(500).json({ err })
}
}
Step 5 - Redirect
We've already coded the redirect
above and have no view to code.
Test the create
Functionality
You should now be able to submit movies - congrats!
Now that we've created a movie or two, let's see how we use Mongoose models to read documents from a MongoDB collection...
👀 Do you need to sync your code?
git reset --hard origin/sync-4-create-functionality
13. Use a Model to Read Data
The querying ability of Mongoose is very capable. For example:
const movies = await Movie.find({ mpaaRating: 'PG' })
.where('releaseYear').lt(1970)
.where('cast').in('Bob Hope')
.sort('-title')
.limit(3)
.select('title releaseYear');
The above query builder syntax is unique to Mongoose and is not available in MongoDB.
Powerful? Yes, but we're going to start with the basics!
Useful Model methods to query for data
Here are the common Model methods for querying data:
Method | Purpose | Syntax |
---|---|---|
find | Returns an array of all documents matching the query object | Movie.find({mpaaRating: 'PG'}, function(err, movies) {...}) |
findById | Find a document based on it's _id | Movie.findById(req.params.id, function(err, movie) {...}) |
findOne | Find the first document that matches the query object | Movie.findOne({releaseYear: 2000}, function(err, movie) {...}) |
Note: An empty query object,
{}
, selects ALL documents.
14. Implement index
Functionality
If you're feeling up to it, pause the video and give the following exercise that implements the movies index
functionality a go.
However, it's fully understandable if you'd like to follow along with me!
💪 Practice Exercise - Implement the movies index
functionality that displays the list of movies (20 mins)
- Identify the RESTful route
-
Use Insonmnia to trigger the request
-
Define the RESTful route
-
Stub up and export the movie controller's
index
action. -
Code the
index
action to:- Use the
Movie
model to query for all movies. As mentioned above, use an empty query object to retrieve all documents.
- Use the
SKIP creating the UI
-
Create a views/movies/index.ejs view to display in an HTML table. Here's most of the markup - please complete according to the comments:
views/movies/index.ejs<%- include('../partials/header') %>
<h2>Movie List</h2>
<table>
<thead>
<tr>
<th>Title</th>
<th>Release Year</th>
<th>Rating</th>
<th>Cast</th>
<th>Now Showing</th>
</tr>
</thead>
<tbody>
<!-- Write the line of EJS to iterate over movies using forEach -->
<tr>
<td><%= m.title %></td>
<td><%= m.releaseYear %></td>
<td><%= m.mpaaRating %></td>
<td><%= m.cast.join(', ') %></td>
<!-- finish the ternary expression to display 'Yes' or 'Nope' -->
<td><%= m.nowShowing ? %></td>
</tr>
<!-- Close the forEach using EJS here -->
</tbody>
</table>
<%- include('../partials/header') %>
Now a quick refactor...
Refactor the create
Action's Redirect
Now that we have an index
view, let's update the redirect
in the create
action:
async function create() {
...
try {
await Movie.create(req.body);
// Always redirect after CUDing data
// We'll refactor to redirect to the movies index after we implement it
res.redirect('/movies'); // Update this line
...
}
Congrats on implementing new
and create
functionality!
👀 Do you need to sync your code?
git reset --hard origin/sync-5-index-functionality
Further Study or Code-Along?
We always encourage practicing expanding your learning on your own by reviewing the Further Study sections.
However, this time, the option is yours, you can stop the video and implement default values and validation on your own, or feel to follow along as I take you through it!
15. Further Study
Defining Default Values for a Property
If a certain property is not provided when creating data, it's possible to specify in the schema a default value to use.
To add a default value, we need to switch from this simple property definition syntax:
const movieSchema = new Schema({
title: String,
releaseYear: Number,
...
To this object syntax:
const movieSchema = new Schema({
title: String,
releaseYear: { type: Number },
...
Now we can add a default
key to specify a default value:
const movieSchema = new mongoose.Schema({
title: String,
releaseYear: { type: Number, default: 2000 },
mpaaRating: String,
cast: [String],
nowShowing: { type: Boolean, default: false }
});
Silly example defaulting the release year to 2000 - yes. But that's how we can add a simple default value.
Note that defaults for array types will not work - they require the use of Mongoose middleware to set default values.
Test it out in the app and we'll find that it didn't work for the releaseYear
because req.body.releaseYear
exists (albeit its value is an empty string) and this prevents the default from being assigned.
We can fix this in the create
action by deleting any property in req.body
that is an empty string:
if (req.body.cast) req.body.cast = req.body.cast.split(/\s*,\s*/);
// Remove empty properties so that defaults will be applied
for (let key in req.body) {
if (req.body[key] === '') delete req.body[key];
}
Now if we don't type in a value in the form for the releaseYear
property, the default of 2000
will be set.
Using a function to provide a default value
You've seen how to add a simple default value, but we can also provide a function as well.
The property's default would then be set to the value returned by the function!
For example, we can take our silly default for releaseYear
and make it default to the current year like this:
const movieSchema = new mongoose.Schema({
title: String,
releaseYear: {
type: Number,
default: function() {
return new Date().getFullYear();
}
},
mpaaRating: String,
cast: [String],
nowShowing: { type: Boolean, default: true }
}, {
timestamps: true
});
Defining Validations for a Property
Validations are used to prevent bogus data, i.e., data that does not conform to the schema, from being saved in the database.
There are several built-in validators we can use.
However, endless flexibility is possible with custom asynchronous and synchronous validator functions and/or Mongoose middleware.
As always, let's take a look at the basics at first...
Movies should not be allowed to be created without a title
. Let's make it required:
const movieSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
...
Now, if we try saving a movie without a title
a validation error will occur and the data will not be saved.
👀 As a reminder, we coded the
create
action to render an error message in thenew.ejs
view if an error occurs.
For properties that are of type Number, we can specify a min
and max
value:
const movieSchema = new mongoose.Schema({
...
releaseYear: {
type: Number,
default: function() {
return new Date().getFullYear();
},
min: 1927
},
...
Cool - no more silent movies!
For properties that are of type String, we have:
enum
: String must be in the provided listmatch
: String must match the provided regular expressionmaxlength
andminlength
: Take a guess :)
Here is how we use the enum
validator on the mpaaRating
property:
const movieSchema = new mongoose.Schema({
...
mpaaRating: {
type: String,
enum: ['G', 'PG', 'PG-13', 'R']
},
...
Note: Some of you may think the above validation is unnecessary because the user is restricted by the choices in the form's
<select>
, however, it's quite easy to bypass such client-side validation - so, it's always a good idea to validate on the server.
👀 Do you need to sync your code?
git reset --hard origin/sync-6-finish-intro