CRUD, MVC, REST, INDUCES and JSX: Remember where we are going

Our Applications have 7 Restful Routes that can be described with the acronym INDUCES
- INDEX
- NEW
- DELETE
- UPDATE
- CREATE
- EDIT
-
SHOW
The Purpose Behind these routes is to create an organized system for Handling CRUD functionality on A Given Resource
These Are Your 7 RESTful REST standing for Representational State Transfer
i.e.
| URL | HTTP Verb | Action | Notes | Mongoose Method | View |
|---|---|---|---|---|---|
| /fruits/ | GET | index | INDEX when a user types localhost:3000/fruits in browser this route shows a list or index of all fruits |
Fruit.find | Index.jsx |
| /fruits/new | GET | new | NEW when a user types localhost:3000/fruits/new in browser this route shows the user a form to create a NEW fruit |
none | New.jsx |
| /fruits/:id | DELETE | destroy | DELETE initiates a delete request through a form submission with action = http://localhost:3000/fruits/:idOfFruit and allows the application the ability to delete a fruit |
Fruit.findByIdAndRemove or Fruit.findByIdAndDelete | none |
| /fruits/:id | PUT | update | UPDATE initiates a put request through a form submission with action = http://localhost:3000/fruits/:idOfFruit and allows the application the ability to Update data about a fruit |
Fruit.findByIdAndUpdate or Fruit.findOneAndUpdate | none |
| /fruits | POST | create | CREATE initiates a post request through a form submission with action = http://localhost:3000/fruits/ and allows the application the ability to Create a fruit |
Fruit.create | none |
| /fruits/:id/edit | GET | edit | EDIT when a user types localhost:3000/fruit/:idOfFruit/edit in browser shows the user a form to edit a fruit |
Fruit.findOne or Fruit.findById | Edit.jsx |
| /fruits/:id | GET | show | SHOW when a user types localhost:3000/fruit/:idOfFruit shows the user an Individual fruit in the browser |
Fruit.findOne or Fruit.findById | Show.jsx |
What is Middleware?
-
In the Intro to Express lesson, we identified the three fundamental capabilities provided by web application frameworks:
- The ability to define routes
- The ability to process HTTP requests using middleware
- The ability to use a view engine to render dynamic templates
- We've already defined routes and rendered dynamic templates.
- In this lesson we complete the trifecta by processing requests using middleware.
- A middleware is simply a function with the following signature:
function(req, res, next) {}First thing we will do is make that annoying lazy middleware we have been using all this time finally do something for us
app.use((req, res, next) => {
res.locals.data = {}
next()
})Moving Over To A Routes and Controllers Folder
Create a Controllers Folder routeController.js and add the following code
const express = require('express');
const router = express.Router();
const Fruit = require('../models/fruit.js');
// add routes
// Index
router.get('/', (req, res) => {
// Use Fruits model to get all Fruits
Fruit.find({}, (error, allFruits) => {
res.render('fruits/Index', {
fruits: allFruits
})
});
});
// New
router.get('/new', (req, res) => {
res.render('fruits/New');
});
// Delete
router.delete('/:id', (req, res) => {
// Delete document from collection
Fruit.findByIdAndRemove(req.params.id, (err, fruit) => {
res.redirect('/fruits');
});
});
// Update
router.put('/:id', (req, res) => {
req.body.readyToEat = req.body.readyToEat === "on" ? true : false;
// Update the fruit document using our model
Fruit.findByIdAndUpdate(req.params.id, req.body, { new: true }, (err, updatedModel) => {
res.redirect('/fruits');
});
});
// Create
router.post('/', (req, res) => {
if (req.body.readyToEat === "on") {
req.body.readyToEat = true;
} else {
req.body.readyToEat = false;
}
// Use Model to create Fruit Document
Fruit.create(req.body, (error, createdFruit) => {
// Once created - respond to client
res.redirect('/fruits');
});
});
// Edit
router.get('/:id/edit', (req, res) => {
// Find our document from the collection - using mongoose model
Fruit.findById(req.params.id, (err, foundFruit) => {
// render the edit view and pass it the found fruit
res.render('fruits/Edit', {
fruit: foundFruit
})
});
});
// Show
router.get('/:id', (req, res) => {
// Find the specific document
Fruit.findById(req.params.id, (error, foundFruit) => {
// render the Show route and pass it the foundFruit
res.render('fruits/Show', {
fruit: foundFruit
});
});
});
// export router
module.exports = router;Then we can use this router inside of our server.js as Middleware
app.use('/fruits', require('./controllers/routeController.js'))We can also further decouple the functionality of our application by making a viewController.js inside of controllers and a dataController.js
const RESOURCE_PATH = '/fruits'
const viewController = {
index(req, res, next){
res.render('fruits/Index', res.locals.data)
},
show(req, res, next){
res.render('fruits/Show', res.locals.data)
},
edit(req, res, next){
res.render('fruits/Edit', res.locals.data)
},
newView(req, res, next){
res.render('fruits/New')
},
redirectHome(req, res, next){
res.redirect(RESOURCE_PATH)
},
redirectShow(req, res, next){
res.redirect(RESOURCE_PATH + `/${req.params.id}`)
}
}
module.exports = viewControllerdata Controller
const Fruit = require('../models/fruit.js');
const dataController = {
index(req, res, next){
Fruit.find({}, (error, allFruits) => {
if(err){
res.status(404).send({
msg: err.message
})
}else {
res.locals.data.fruits = allFruits
next()
}
});
},
create(req, res, next){
req.body.readyToEat = req.body.readyToEat === "on" ? true : false;
// Use Model to create Fruit Document
Fruit.create(req.body, (error, createdFruit) => {
// Once created - respond to client
if(err){
res.status(404).send({
msg: err.message
})
}else {
res.locals.data.fruit = createdFruit
next()
}
});
},
show(req, res, next){
Fruit.findById(req.params.id, (err, foundFruit)=>{
if(err){
res.status(404).send({
msg: err.message
})
} else {
res.locals.data.fruit = foundFruit
next()
}
})
},
update(req, res, next){
req.body.readyToEat = req.body.readyToEat === "on" ? true : false;
Fruit.findByIdAndUpdate(req.params.id, req.body, { new: true }, (err, updatedFruit) => {
if(err){
res.status(404).send({
msg: err.message
})
} else {
res.locals.data.fruit = updatedFruit
next()
}
});
},
destroy(req, res, next){
Fruit.findByIdAndRemove(req.params.id, (err, fruit) => {
if(err){
res.status(404).send({
msg: err.message
})
} else {
res.locals.data.fruit = fruit
next()
}
});
}
}
module.exports = dataControllerconst express = require('express');
const router = express.Router();
const viewController = require('./viewController.js')
const dataController = require('./dataController.js')
// add routes
// Index
router.get('/', dataController.index, viewController.index);
// New
router.get('/new', viewController.newView );
// Delete
router.delete('/:id', dataController.destroy, viewController.redirectHome);
// Update
router.put('/:id', dataController.update, viewController.redirectShow);
// Create
router.post('/', dataController.create, viewController.redirectHome);
// Edit
router.get('/:id/edit', dataController.show, viewController.edit);
// Show
router.get('/:id', dataController.show, viewController.show);
// export router
module.exports = router;Now lets also clean up our data and put our database code into the models folder, into a file called db.js
/*******
Database Setup
******/
const mongoose = require('mongoose');
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
})
module.exports = mongoose.connectionNew and Improved server.js
// Import Modules and set up vars
require('dotenv').config()
const express = require('express');
const methodOverride = require('method-override');
const app = express();
const PORT = process.env.PORT || 3000;
//connect to database
const db = require('./models/db')
db.once('open', () => {
console.log('connected to mongo')
})
//Initialize View Engine
app.set('view engine', 'jsx');
app.engine('jsx', require('jsx-view-engine').createEngine())
// Mount Express Middleware
app.use((req, res, next) => {
res.locals.data = {}
next()
}) // Creates res.locals.data
app.use(express.urlencoded({ extended: true })) // Creates req.body
app.use(methodOverride('_method')); // Allows us to override methods
app.use(express.static('public')); // Allows us to have Static Files
app.use('/fruits', require('./controllers/routeController.js')); // Mounts our RESTFUL/INDUCES ROUTES at /fruits
// Listen on PORT
app.listen(PORT, () => {
console.log('We in the building', PORT)
})Future More Advanced Apps
- You would have a controller for each resource/model at minimum
- You would have controllers for special functionality like
authentication controllers - The seperation of concerns makes it easier for multiple teams to work in parallel
Review Questions - What is Node.js?
❓ Is Node.js a programming language?
❓ Is Express a programming language?
❓ What is the primary reason why Node/Express applications are so performant?
❓ Is...const el = document.getElementById('my-list');a valid JavaScript statement in a Node app?
❓ What is a Model?
❓ What is A View Engine?
❓ What is A DataController?
❓ What is A ViewController?
❓ What is A RouteController?
❓ What is Express Middleware?