Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 

Repository files navigation

MEN STACK NOTES

Example Fruits CRUD

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)

The Create Request Cycle

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

Current Project Structure

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

SETUP

  • 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 .

Add node_modules to .gitignore

.gitignore

node_modules
.env

Add the MongoDB connection string to .env:

MONGODB_URI=mongodb+srv://<username>:<password>@<cluster-url>/fruits?retryWrites=true&w=majority

Remember to change your database name in the connection string

Write Server Boilerplate

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 Screenshot 2026-07-05 at 11 22 36 AM

Navigate to http://localhost:3000 to view our server.

Use ctrl + c to stop the server in the terminal.

Creating a Test Route

app.get('/test', function(req, res) {
    res.send('this is a test ✨')
})
Screenshot 2026-07-05 at 12 30 29 PM

Navigate to http://localhost:3000/test

Using request parameters

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

Screenshot 2026-07-05 at 2 12 17 PM

Rendering EJS

  • create a views directory
  • create an .ejs file like home.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 ejs page using a controller like this one:
app.get('/', function(req, res){
    res.render('home.ejs')
})
Screenshot 2026-07-06 at 12 39 56 PM

EJS Syntax

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 %>

Pass data from the controller

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>

Using forEach in ejs

<ul>
    <% inventory.forEach(function(item){ %>
    <li><%= item.name %></li>
    <% }) %>
</ul>
Screenshot 2026-07-06 at 2 59 14 PM

Creating dynamic links to a show page

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.

Reusable Nav bar with partials

  • Create a folder called partials inside of views
  • Create a file called nav.ejs inside of partials

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') %>

Adding CSS & images with public

CSS

  • Create a folder called public

  • Create a folder called stylesheets inside of public

  • Create a file called style.css inside of stylesheets

  • Link the stylesheet in the head of our html files (inside nav partial if we're using partials)

<link rel="stylesheet" href="/stylesheets/style.css">

Images

  • Create an images folder inside of our public folder
  • Link to images like normal: <img src="/images/family.jpg" alt="A happy family">

🥭 Adding MongoDB and Mongoose

Connect to MongoDB

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} 🥭`)
})
Screenshot 2026-07-11 at 10 12 22 AM

Creating a Fruit in the Database

Create a Fruit model

Create a models folder and a fruit.js file:

mkdir models
touch models/fruit.js

Add 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 = Fruit

Import 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 the New Fruit Form

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.isReadyToEat

Display the New Fruit Form

Create 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:

Screenshot 2026-07-11 at 10 42 44 AM Screenshot 2026-07-11 at 10 39 33 AM

Create a Fruit from the Form

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:

  1. Receives the form data through req.body.
  2. Creates a new fruitData object.
  3. Converts the checkbox value into a boolean.
  4. Uses Fruit.create() to save the fruit.
  5. 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: Boolean

We convert the checkbox value before adding it to the database:

if (req.body.isReadyToEat === 'on') {
    fruitData.isReadyToEat = true
} else {
    fruitData.isReadyToEat = false
}

Check the Database

  1. Open MongoDB Atlas.
  2. Select Browse Collections.
  3. Open the fruits_db database.
  4. Open the fruits collection.

We should see a document similar to: Screenshot 2026-07-11 at 10 35 26 AM

MongoDB automatically adds _id and __v.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors