Book List CRUD App with Mongoose - Create & Read
Lesson Objectives
- Review the flow of technology
- Initialize a directory
- Start express
- Create New Route
- Create Create Route
- Connect Express to Mongo
- Create Book Model
- Have Create Route Create data in MongoDB
- Create Index Route
- Have Index Route Render All Books
- Have Create Route redirect to Index After Book Creation
- Create Show Route
- Have Index Page Link to Show Route
- Create show.ejs
Review the flow of technology
Initialize a directory
mkdir booklist
cd booklist
touch server.js
code .
npm init -y
npm install express dotenv
touch .env
Remember to add a .gitignore with node_modules if you don't have a global .gitignore.
Add The PORT Variable to .env
PORT=3000
Create Your Basic Express Server
// Dependencies
const express = require('express');
const app = express();
require('dotenv').config();
// Listener
const PORT = process.env.PORT;
app.listen(PORT, () => console.log(`server is listning on port: ${PORT}`));
Stop! Check your work!
Connect Express to MongoDB Atlas
npm install mongoose
- Navigate to MongoDB.com and create a new database or get prepapred to use an old one.
- Add your connection string to your .env file (do not copy and paste this one) then update the sub-database to use
booklist
like we've done in the connection string example below
Inside .env
DATABASE_URL=mongodb+srv://<username>:<password>@general-assembly.1wjse.mongodb.net/booklist?retryWrites=true&w=majority
Inside server.js
, connect to your database
// Dependencies
const mongoose = require('mongoose');
// Database Connection
mongoose.connect(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
});
// Database Connection Error/Success
// Define callback functions for various events
const db = mongoose.connection
db.on('error', (err) => console.log(err.message + ' is mongo not running?'));
db.on('connected', () => console.log('mongo connected'));
db.on('disconnected', () => console.log('mongo disconnected'));
Stop! Check your work!
Create Create Route
// Create
app.post('/books', (req, res) => {
res.send('received');
});
Stop! Check your work with Postman.
Set Up Your Body Parser Middleware
// Middleware
// Body parser middleware: give us access to req.body
app.use(express.urlencoded({ extended: true }));
app.post('/books', (req, res) => {
res.send(req.body);
});
Stop! Check your work with Postman
Let's add:
title: Cracking the Coding Interview
author: Gayle Laakmann McDowell
Format Checkbox Data Properly
In server.js
:
app.post('/books', (req, res) => {
if (req.body.completed === 'on') {
//if checked, req.body.completed is set to 'on'
req.body.completed = true;
} else {
//if not checked, req.body.completed is undefined
req.body.completed = false;
}
res.send(req.body);
});
Stop! Check your work again by resending the previous request.
Create Books Model
mkdir models
touch models/book.js
- Create the book schema
In models/book.js
:
const mongoose = require('mongoose');
const bookSchema = new mongoose.Schema({
title: { type: String, required: true },
author: { type: String, required: true },
completed: Boolean,
});
const Book = mongoose.model('Book', bookSchema);
module.exports = Book;
Have Create Route Create data in MongoDB
Inside server.js
:
// Dependencies
const Book = require('./models/book.js');
// Routes / Controllers
// Create
app.post('/books', (req, res) => {
if (req.body.completed === 'on') {
//if checked, req.body.completed is set to 'on'
req.body.completed = true;
} else {
//if not checked, req.body.completed is undefined
req.body.completed = false;
}
Book.create(req.body, (error, createdBook) => {
res.send(createdBook);
});
});
Stop! Check your work with Postman.
Create The New Page Route
Remember INDUCES (index, new, delete, update, create, edit, show) to help organize your routes and avoid any conflicts.
// Routes / Controllers
// New
app.get('/books/new', (req, res) => {
res.send('new');
});
Create The New View
npm install ejs
mkdir views
touch views/new.ejs
In views/new.ejs
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Book List</title>
</head>
<body>
<h1>New Book Page</h1>
<form action="/books" method="POST">
Title: <input type="text" name="title" /><br />
Author: <input type="text" name="author" /><br />
Completed: <input type="checkbox" name="completed" /><br />
<input type="submit" name="" value="Add Book" />
</form>
</body>
</body>
</html>
Render the view
// Routes / Controllers
// New
app.get('/books/new', (req, res) => {
res.render('new.ejs');
});
Stop! Check your work! Is the view working? We'll check the form functionality soon!
Create The Index Route
Remember INDUCES
In server.js
:
// Index
app.get('/books', (req, res) => {
res.send('index');
});
Stop! Check your work.
Create The Index View
touch views/index.ejs
In views/index.ejs
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Book List</title>
</head>
<body>
<h1>My Reading List</h1>
</body>
</html>
Render The Index View
// Index
app.get('/books', (req, res) => {
res.render('index.ejs');
});
Have Index Route Pass Book Data to the View
// Index
app.get('/books', (req, res) => {
Book.find({}, (error, allBooks) => {
res.render('index.ejs', {
books: allBooks,
});
});
});
We could just res.send allBooks and check with Postman, but we're getting confident and bold! Let's stick with a render for now and check it in a moment.
Update the Index View to Display All Books:
In views/index.ejs
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>Books index page</h1>
<ul>
<% books.forEach(book => { %>
<li><%=book.title; %></li>
<% }) %>
</ul>
</body>
</html>
We could use any type of loop we want, but the forEach loop reads really nicely!
Add a Link to the Create Page Above Your h1:
In views/index.ejs
:
<nav>
<a href="/books/new">Add a New Book</a>
</nav>
Update Your Create Route redirect to Index After Book Creation
In server.js
:
Book.create(req.body, (error, createdBook) => {
res.redirect('/books');
});
Stop! Check your work. Navigate to your form.
Let's add:
title: You don't know JS Yet
author: Kyle Simpson
We should be redirected to /books and see our new book there. Success? Awesome! Error? Take a moment to fix it.
Have Index Page Link to Show Route
In views/index.ejs
:
<li>
<a href="/books/<%=book._id; %>"> <%=book.title; %> </a>
</li>
Notice how we have to put an underscore before id. That's a MongoDB convention.
Stop! Check your work. Click one of the links and make sure it redirects you to the right place.
Create Show Route
Remember INDUCES
// Show
app.get('/books/:id', (req, res) => {
Book.findById(req.params.id, (err, foundBook) => {
res.send(foundBook);
});
});
Stop! Check your work.
Create A Show Page
touch views/show.ejs
In views/show.ejs
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<nav>
<a href="/books">Back to Books Index</a>
</nav>
<h1><%=book.title %></h1>
Author: <%= book.author%>
<br />
Completed: <%= book.completed %>
</body>
</html>
Render the Show Page
// Show
app.get('/books/:id', (req, res) => {
Book.findById(req.params.id, (err, foundBook) => {
res.render('show.ejs', {
book: foundBook,
});
});
});
Final stop! Check your work :)