7. Mongoose + Express
Mongoose + Express
-
Empty app
npm init -y
-
install express, ejs and mongoos
-
create folder views and index.js
-
add express and mongoose
const express = require ('express')
const app = express()
const path = require('path')
const mongoose = require('mongoose');
main().catch(err => console.log(err));
async function main() {
await mongoose.connect('mongodb://localhost:27017/shopApp'); //create a db named test
console.log('connection open')
}
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.listen(3000, () =>{
console.log('app is listening on port 3000')
})
app.get('/dog', (req, res)=>{
res.send('woof')
})
- create folder models and product.js
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true,
min: 0
},
category: {
type: String,
enum: ['fruit','vegetable', 'dairy']
}
})
const Product = mongoose.model('Product', productSchema) //creates a collection named movies
module.exports = Product;
- create some dummy data in seed.js
const mongoose = require('mongoose');
const Product = require('./models/product');
main().catch(err => console.log(err));
async function main() {
await mongoose.connect('mongodb://localhost:27017/farmStand'); //create a db named test
console.log('connection open')
}
const seedProducts = [
{ name: 'eggplan',
price: 1.00,
category: 'vegetable'
},
{ name: 'melon',
price: 4.99,
category: 'fruit'
},
{ name: 'watermelon',
price: 4.99,
category: 'fruit'
},
{ name: 'celery',
price: 3.99,
category: 'vegetable'
},
{ name: 'chocolate milk',
price: 1.50,
category: 'dairy'
},
{ name: 'cheese',
price: 7.99,
category: 'dairy'
}
]
Product.insertMany(seedProducts)
.then(res => console.log(res))
.catch(e => console.log(e))
-
run
node seed.js
to save the data in the database -
check the data has been saved
show dbs
use farmStand
show collections
db.products.find()
-
setup a route to display the products
app.get('/products', async (req, res)=>{
const products = await Product.find({})
//res.send('all products will be here')
res.render('products/index', {products})
})
- create a folder products in views and index.ejs
<body>
<h1>All Products</h1>
<ul>
<% for (let product of products){%>
<li> <%= product.name %> </li>
<% } %>
</ul>
</body>
- setup a show route using find by id
app.get('/products/:id', async (req, res) => {
const {id} = req.params;
const product = await Product.findById(id);
//res.send('details page')
//console.log(product);
res.render('products/show', {product})
})
- show.ejs
<body>
<h1><%= product.name %></h1>
<ul>
<li>Price: <%= product.price %></li>
<li>Category: <%= product.category %></li>
</ul>
</body>
- add links in the index page
<ul>
<% for (let product of products){%>
<li> <a href="/products/<%= product._id %>"><%= product.name %></a> </li>
<% } %>
</ul>
- add new form
<h1>Add Product</h1>
<form action="/products" method="POST">
<label for="name">Product Name</label>
<input type="text" name="name" id="name" placeholder="product name">
<label for="price">Price</label>
<input type="number" name="price" id="price" placeholder="product price">
<label for="category">Category</label>
<select name="category" id="category">
<option value="fruit">fruit</option>
<option value="vegetable">vegetable</option>
<option value="dairy">dairy</option>
</select>
<button>Submit</button>
</form>
//parse response
app.use(express.urlencoded({extended: true}))
app.get('/products/new', (req, res) => {
res.render('products/new')
})
app.post('/products', async (req, res) =>{
const newProduct = new Product(req.body)
await newProduct.save();
console.log(newProduct);
res.redirect(`/products/${newProduct._id}`);
})
- Updating products - install method-override
const methodOverride = require ('method-override')
app.use(methodOverride('_method'));
<form action="/products/<%= product._id%>?_method=PUT" method="POST">
app.get('/products/:id/edit', async (req, res)=>{
const {id} = req.params;
const product = await Product.findById(id)
res.render('prodcuts/edit', {product})
})
- update data in mongoose db
app.put('/products/:id', async (req, res)=>{
const {id} = req.params;
const product = await Product.findByIdAndUpdate(id, req.body, {runValidators: true, new: true});
res.redirect(`/products/${product._id}`);
})
<a href="/products/<%= product._id %>/edit">Edit</a>
- Fix category selector in edit.js page
<option value="fruit"
<%=product.category === 'fruit' ? 'selected' : ''%>
>fruit</option>
Best to use a loop:
- In index.js create a list of categories and pass it to the new.ejs and edit.js routes:
const categories = ['fruit', 'vegetable', 'dairy', 'fungi'];
app.get('/products/new', (req, res) => {
res.render('products/new', {categories})
})
app.get('/products/:id/edit', async (req, res)=>{
const {id} = req.params;
const product = await Product.findById(id)
res.render('products/edit', {product, categories})
})
- Create a loop in new.js
<select name="category" id="category">
<%for(let category of categories){%>
<option value="<%= category %>"><%= category %></option>
<%}%>
</select>
- and another one in the edit.js
<select name="category" id="category">
<%for(let category of categories){%>
<option value="<%= category %>" <%= product.category === category ? 'selected' : '' %>
><%= category %></option>
<%}%>
</select>
- Delete products
<form action="/products/<%=product._id%>?_method=DELETE" method="POST">
<button>Delete</button>
</form>
app.delete('/products/:id', async (req, res)=>{
const {id} = req.params;
const deletedProduct = await Product.findByIdAndDelete(id);
res.redirect('/products/');
})
- Filter by Category
- Add a link to the category value in the show.ejs page
<li>Category: <a href="/products/?category=<%=product.category%>"><%=product.category%></a></li>
- index.js
<% if(category !== 'All') { %>
<a href="/products">All products</a>
<%}%>
app.get('/products', async (req, res)=>{
const {category} = req.query;
if (category){
const products = await Product.find({category});
res.render('products/index', {products, category})
}
else{
const products = await Product.find({})
res.render('products/index', {products, category: 'All'})
}
})
Written on December 27, 2021