| HTTP Method |
URL Endpoint |
Purpose |
|---|---|---|
| GET | /fruits/new | View a form for submitting a fruit (be sure to define this route before the show route) |
| POST | /fruits | Handle the new fruit form being submitted |
| GET | /fruits | View all the fruits |
| GET | /fruits/:fruitId | View the details of any fruit |
| DELETE | /fruits/:fruitId | Delete a fruit (restrict to user who submitted the fruit) |
| GET | /fruits/:fruitId/edit | View a form for editing a fruit (restrict to user who submitted the fruit) |
| PUT | /fruits/:fruitId | Handle the edit fruit form being submitted (restrict to user who submitted the fruit) |
When the user creates a fruit, the request follows these steps:
GET /fruits/new
↓
Express renders new.ejs
↓
The user completes the form
↓
The form sends POST /fruits
↓
express.urlencoded() creates req.body
↓
The controller converts the checkbox value
↓
Fruit.create(req.body) saves the document
↓
Express redirects to /fruits/new
At this point, the application should will a structure like this:
men-stack-crud-app-fruits/
├── models/
│ └── fruit.js
├── public/
│ ├── images/
│ └── stylesheets/
│ └── style.css
├── views/
│ ├── partials/
│ │ └── nav.ejs
│ ├── new.ejs
│ └── home.ejs
├── .env
├── .gitignore
├── package-lock.json
├── package.json
└── server.js
- create a directory
- create our first files
touch server.js .gitignore .env - initialize a node project with
npm init -y - install applications
npm i express morgan ejs mongoose dotenv - open our project with
code .
.gitignore
node_modules
.envMONGODB_URI=mongodb+srv://<username>:<password>@<cluster-url>/fruits?retryWrites=true&w=majority
Remember to change your database name in the connection string
server.js
const express = require('express')
const morgan = require('morgan')
const path = require('path')
const app = express()
app.use(express.static(path.join(__dirname, "public"))) // use static middleware with other middlware like morgan
app.use(express.urlencoded({ extended: false })) // need this for reading form data
app.use(morgan('dev'))
app.listen(3000, function(){
console.log('Listening on port 3000 💛')
})Run the server with nodemon server.js

Navigate to http://localhost:3000 to view our server.
Use ctrl + c to stop the server in the terminal.
app.get('/test', function(req, res) {
res.send('this is a test ✨')
})
Navigate to http://localhost:3000/test
app.get('/:userId', function(req, res){
res.send(`Hello user number:${req.params.userId}`)
console.log(req.params.userId)
})Navigate to http://localhost:3000/2490
- create a
viewsdirectory - create an
.ejsfile likehome.ejs - add html boilerplate with
!
home.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
</head>
<body>
<h1>This is an ejs page! 🚀</h1>
</body>
</html>- render
ejspage using a controller like this one:
app.get('/', function(req, res){
res.render('home.ejs')
})
To use javaScript in an ejs file, I need a scriplet tag:
<% let user = 'nabila' %>To display javaScript values from an ejs file, I need an output tag:
<%= user %>Use the locals object inside the render method:
res.render('home.ejs', {
title: 'Home Page',
})Now I can use the title variable in my home.ejs file.
home.ejs
<h1><%= title %></h1><ul>
<% inventory.forEach(function(item){ %>
<li><%= item.name %></li>
<% }) %>
</ul>
item.name is dynamically showing up. The link is also dynamically changing with the item. (see forEach above)
<a href="/<%= item.id %>"> <%= item.name %> </a>We should see the URL change in the browser.
- Create a folder called
partialsinside ofviews - Create a file called
nav.ejsinside ofpartials
Should include the opening html, head, and body tags:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
</nav>Include the nav in other files with this statement:
<%- include('./partials/nav') %>-
Create a folder called
public -
Create a folder called
stylesheetsinside ofpublic -
Create a file called
style.cssinside ofstylesheets -
Link the stylesheet in the head of our
htmlfiles (insidenavpartial if we're using partials)
<link rel="stylesheet" href="/stylesheets/style.css">- Create an
imagesfolder inside of ourpublicfolder - Link to images like normal:
<img src="/images/family.jpg" alt="A happy family">
At the top of server.js, load dotenv and Mongoose:
const dotenv = require('dotenv').config()
const mongoose = require('mongoose')Connect to MongoDB after const app = express():
const app = express()
// connect to mongoDB
mongoose.connect(process.env.MONGODB_URI)
mongoose.connection.on('connected', () => {
console.log(`Connected to MongoDB ${mongoose.connection.name} 🥭`)
})
Create a models folder and a fruit.js file:
mkdir models
touch models/fruit.jsAdd the Fruit schema and model:
const mongoose = require('mongoose')
const fruitSchema = new mongoose.Schema({
name: String,
isReadyToEat: Boolean,
})
const Fruit = mongoose.model('Fruit', fruitSchema)
module.exports = FruitImport the model into server.js after your MongoDB Connection string:
console.log(`Connected to MongoDB ${mongoose.connection.name} 🥭`)
})
// import Fruit model here
const Fruit = require('./models/fruit.js')Create new.ejs inside the views folder:
views/
├── home.ejs
└── new.ejs
Add the form:
<h1>Add a Fruit</h1>
<form action="/fruits" method="POST">
Fruit name:
<input type="text" name="name">
Ready to eat?
<input type="checkbox" name="isReadyToEat">
<button type="submit">Add Fruit</button>
</form>The input name values become keys inside req.body.
For example:
req.body.name
req.body.isReadyToEatCreate the new route:
// GET /fruits/new
app.get('/fruits/new', async (req, res) => {
res.render('new.ejs')
})Visit:
http://localhost:3000/fruits/new
The same form without and with CSS:
Create the POST /fruits route:
// POST /fruits
app.post('/fruits', async (req, res) => {
const fruitData = {}
fruitData.name = req.body.name
// Converts the 'on' into true for our isReadToEat boolean on our model
if (req.body.isReadyToEat === 'on') {
fruitData.isReadyToEat = true
} else {
fruitData.isReadyToEat = false
}
const createdFruit = await Fruit.create(fruitData)
res.redirect('/')
})The route:
- Receives the form data through
req.body. - Creates a new
fruitDataobject. - Converts the checkbox value into a boolean.
- Uses
Fruit.create()to save the fruit. - Redirects the user to the home page.
Why We Convert the Checkbox
A checked HTML checkbox sends the string:
'on'An unchecked checkbox does not send a value.
Our schema (model) expects a boolean:
isReadyToEat: BooleanWe convert the checkbox value before adding it to the database:
if (req.body.isReadyToEat === 'on') {
fruitData.isReadyToEat = true
} else {
fruitData.isReadyToEat = false
}- Open MongoDB Atlas.
- Select Browse Collections.
- Open the
fruits_dbdatabase. - Open the
fruitscollection.
We should see a document similar to:

MongoDB automatically adds _id and __v.