Hướng Dẫn Xây Dựng Web CRUD Books Với NodeJS, Express và MongoDB

Giới Thiệu Chung
Hôm nay mình sẽ hướng dẫn các bạn xây dựng một web CRUD Books với NodeJS, Express và MongoDB.
CRUD là gì ?
CRUD có nghĩa là Create, Read, Update và Delete. Bài viết này mình sẽ hướng dẫn các bạn tạo, đọc, cập nhật và xóa dữ liệu cuốn sách,..
Mục Đích Bài Viết
Giúp các bạn ôn lại các kiến thức về NodeJS, cách xây dựng cũng như truy vấn database MongoDB.
Nắm vững các kiến thức và cách hoạt động về Get, Post request.
Sau bài viết này các bạn có thể tự xây dựng cho mình một web app tương tự như: todo list,...Đó cũng là một cách giúp các bạn có thể nắm vững các kiến thức được học.
Mình mong muốn các bạn đọc bài một cách tỉ mỉ, đọc không lướt nha.
Bắt Đầu Thôi Nào
Lên Ý Tưởng Về Web CRUD Books
Trước khi các bạn làm một cái gì đó thì trước hết phải hình thành ý tưởng, sau đó triển khai ý tưởng. Điều này giúp các bạn làm việc một cách có logic và khoa học hơn. Trước hết mà chúng ta bắt tay vào code thì mình sẽ giúp các bạn nắm rõ hơn ý tưởng mình cần làm nha. Ý tưởng cái này cũng đơn giản thôi nha.
Chúng ta muốn tạo book thì sẽ tạo ra các trường khi mình nhập dữ liệu vào thì sẽ lưu vào database.
Muốn hiển thị các book mà mình đã tạo thì chúng ta sẽ find dữ liệu trong database sau đó hiển thị ra màn hình.
Muốn cập nhật book chúng ta sử dụng ID để cập nhật nha. Mỗi book sẽ có mỗi id riêng chúng ta mà muốn chỉnh sửa chỉ cần find ID book cần chỉnh sửa dữ liệu rồi sau đó lưu lại database.
Còn muốn xóa thì chỉ cần nhập ID book muốn xóa sau đó xóa book đó khỏi database thôi.
Ở đây mình chỉ nói sơ qua thôi để cho các bạn có thể hình dung ra được mình cần làm gì thôi nha.
Cài Đặt Và Thiết Lập
Trước tiên các bạn tạo cho mình một folder trong folder đó là nơi chứa các thư mục và chương trình mà chúng ta dùng để viết cho ứng dụng CRUD books.
Cài đặt các module để thiết lập ứng dụng
Để mà cài các module trước hết các bạn phải cài đặt NodeJS tại đây.
- npm install --save express-generator
Module này nó sẽ giúp chúng ta tạo nhanh khung sườn cho ứng dụng. - express --view=pug
Nó sẽ tạo cho các bạn phần view engine và được setup là Pug. - npm install nodemon --save
Tự động reload lại server khi bạn thay đổi code. Để khởi chạy server các bạn thêm"devStart": "nodemon app.js"
vào file package.json nha. Các bạn mở terminal lên sau đó gõnpm run devStart
để khởi chạy server nha. - npm install mongoose --save
Mongoose là một Object Document Mapper (ODM). Điều này có nghĩa là Mongoose cho phép bạn định nghĩa các object (đối tượng) với một schema được định nghĩa rõ ràng. - npm install dotenv --save
Dotenv là một biến môi trường dùng để bảo mật các thông tin quan trọng như username, password, url database,...Nếu chúng ta không lưu những thông tin mật vào file .env thì khi push source code lên github thì ai cũng có thể vào xem được và ai cũng biết username, password thì sẽ bị người khác chiếm đoạt và đánh cắp tài liệu rất là nguy hiểm nha.
Bắt Đầu Code Thôi Lào
Thiết Lập Web Server
Trong file app.js các bạn bỏ cho mình router user và tạo cổng port cho web server. Trong folder routes các bạn cũng bỏ cho mình file user.js
nha, đó là các file mặc định khi mình chạy npm install --save express-generator
không cần thiết thì mình bỏ nha.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var port = 5555;
var indexRouter = require('./routes/index');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// setup router
app.use('/', indexRouter);
// catch 404 and forward to error handler
.........
.........
.........
// error handler
.........
.........
.........
app.listen(port,function(){
console.log("Server listening connect port" + port)
})
module.exports = app;
Trong thư mục router có file index.js, các bạn tạo cho nó một Route handlers và thử chạy sever xem sau. Mục đích để các bạn test xem server có chạy không nha.
router.get('/',function(req, res, next){
res.send('Hello Everyone')
})

Thiết Lập Database
Cài đặt mongodb download tại đây, các bạn tạo cho mình một file .env
bên trong thư mục gốc nha. Trong file này các bạn tạo một đường dẫn là DB_URL=mongodb://localhost/CRUD_books
Trong file app.js các bạn khai báo module dotenv với module mongoose và tạo đường dẫn mongodb.
// Khai báo dotenv
require('dotenv').config()
// path database
var mongoose = require('mongoose');
mongoose.connect(process.env.DB_URL,{useNewUrlParser:true, useUnifiedTopology: true });
Bây giờ mà các bạn bạn muốn chạy được server thì phải khơi chạy database nha.
Định Nghĩa Các Schema Books Cho Mongoose
Bên trong thư mục gốc các bạn tạo cho mình thư mục modal
bên trong thư mục modal
các bạn tạo cho mình file book.modal.js
.
Trong file book.modal.js
cần tạo một đối tượng book
và sẽ chứa ba thuộc tính là title
, description
và author
.
var mongoose = require('mongoose');
var book = new mongoose.Schema({
title: { type: String, required: true },
description: { type: String, required: true },
author: { type: String, required: true }
})
// Biên dịch mô hình từ schema
module.exports = mongoose.model('books', book);
Tham số thứ nhất là tên riêng cho collection sắp được tạo ra cho mô hình của bạn, và tham số thứ hai là schema mà bạn muốn dùng để tạo ra mô hình.
Định Nghĩa Các Router Cho CRUD Books
Trong file index.js
nằm bên trong thư mục routes, tạo các phương thức để xử lý các hoạt động CRUD. Các bạn khai báo cho mình modal của Books nha.
var Book = require('../modal/book.modal')
Tạo Books
Giao diện để tạo book
Trước tiên để tạo book các bạn cần phải có một giao diện để điền các thông tin cần có để tạo book.
Trong thư mục views
có file index.pug
. Trong file đó các bạn tạo cho mình bộ khung gồm có form có chứa các thẻ label, input để điền các thông tin cần có để tạo book bao gồm: title, description và author.
Các bạn nhớ điền đúng các giá trị trong thuộc tính name trong thẻ input trùng với các thuộc tính trong file modal nha.
extends layout
block content
div.title-book
h1 CRUD BOOKS
//- Create books
div.content_book
h2 Create Books
form(action="/" method="POST")
div
label(for="tittle") Title
input(type="text" name="title" required="required")
div
label(for="tittle") Description
input(type="text" name="description" required="required")
div
label(for="tittle") Author
input(type="text" name="author" required="required")
div
button(type="submit") Create
Routes Get/
Để mà có thể load được giao diện ra và tạo book thì chúng ta cần phải render nó ra bằng phương thức Get
đúng không nào.
router.get('/', function (req, res, next) {
res.render('index')
});
Routes Post/
Sau khi chúng ta đã tạo giao diện cho nó xong rồi thì tiếp đến. Trong thư mục routes
có file index.js
. Các bạn tạo cho mình phương thức POST
để khi submit dữ liệu thì nó sẽ được gửi lên server và lưu trong database nha.
// Create books
router.post('/', function (req, res, next) {
var newbook = new Book()
newbook.title = req.body.title;
newbook.description = req.body.description;
newbook.author = req.body.author;
newbook.save().then(function (err) {
if (err) { console.log(err) }
})
res.redirect('/')
})
Mình giải thích ở đây một chút là khi mình post ngầm dữ liệu để tạo book thì nó sẽ tạo ra một cái object rỗng và gán vào biến newbook, object rỗng này được tạo ra từ thằng new thì trong object này sẽ chứa các title, description và author mà chúng ta đã req.body để truy xuất dữ liệu. Sau đó lưu nó vào database.
Bây giờ chúng ta cùng xem thử giao diện và nguyên lý hoạt động có đúng như chúng ta mong đợi không nha.
Thì như chúng ta đã thấy thì giao diễn nó khá chi là củ chuối bởi vì pug nó cũng giống như html thôi nó cũng chỉ xây khung sườn là chính thôi còn muốn đẹp thì phải css cho nó. Phần css mình sẽ làm ở phần sau nha.
Bây giờ các bạn xem thử trong database nó có lưu lại không nha. Nếu nó có lưu thì xem như phần tạo book coi như hoàn thành được 90% rồi giờ chỉ còn css cho nó thôi là ổn áp ngay.
Đọc Books
Đọc books có nghĩa là chúng ta in ra các dữ liệu của books mà mình nhập và lưu trong database như: title, description, author.
Routes Get/
Các bạn muốn hiển thị ra các dữ liệu của book thì chúng ta sử dụng phương thức Get
. Chúng ta vừa Get giao diện cũng vừa Get ra dữ liệu.
Các bạn find dữ liệu book trong database ra nha. Sử dụng async, await để find dữ liệu.
Async await mục đích chính của nó là chuyển bất đồng bộ thành đồng bộ, nó sẽ chạy từ trên xuống dưới, nó sẽ find dữ liệu rồi mới đến việc render.
Nên các bạn gán cho nó một biến để có thể sử dụng để render ra được dữ liệu ra nha.
// Read books
router.get('/', async function (req, res, next) {
let books = await Book.find()
res.render('index', { books: books })
});
Giao diện để đọc dữ liệu của book
Tiếp đến bây giờ chúng ta phải hiển thị dữ liệu của nó ra. Trong thư mục views
có file index.pug
, chúng ta sẽ lặp qua biến books mà mình đã khai báo trong res.render để có thể truy vấn vào database và hiển thị ra ngoài màn hình.
Trong phần read book các bạn hiển thị ra bốn thuộc tính nha đó là: title, description, author và id. Tại sao lại hiển thị ra id, mục định của nó hiển thị ra để có thể lấy giá trị id làm các phần cập nhật books và delete books, các bạn theo dõi các phần tiếp theo để biết thêm chi tiết.
//- Read books
div.content_book
h2 Read Books
div.list_books
each book in books
ul
li
span Title: 
=book.title
li
span Description: 
=book.description
li
span Author: 
=book.author
li
span ID: 
=book._id
Chúng ta cùng xem thử nó có hiển thị dữ liệu của book ra không nha.
Cập Nhật Books
Mình cũng nó sơ qua nguyên lý hoạt động là sẽ sử dụng id để cập nhật book, sử dụng id như thế nào thì đơn giản thôi chúng ta sẽ copy cái id mà mình đã hiển thị ra trong phần đọc books sao đó dán vào phần cập nhật bookks. sau khi dán vào thì chúng ta sẽ phải req.body thẻ id đó.
Rồi tiếp đến sẽ find id trong database có trùng với id mà mình nhập không, cuối cần các bạn chỉ cần thay đổi dữ liệu rồi lưu vào database thôi.
Giao diện để cập nhật dữ liệu book
Để mà có thể cập nhật được books thì chúng ta cần phải tạo cho nó một cái giao diện để có thể cập nhật books nó cũng tương tự như thằng tạo books thôi chỉ khác chỗ là thêm một trường id nữa thôi.
Lưu ý: Và các bạn nên nhớ là trong form thì action của thằng tạo books phải khác thằng cập nhật books và thằng xóa books nhỡ để nó không phải bị trùng nhau gây nhiễu loạn thông tin,..
//- Update books
div.content_book
h2 Update Books
form(action="/update" method="POST")
div
label(for="tittle") ID
input(type="text" name="id" required="required")
div
label(for="tittle") Title
input(type="text" name="title" required="required")
div
label(for="tittle") Description
input(type="text" name="description" required="required")
div
label(for="tittle") Author
input(type="text" name="author" required="required")
button(type="submit") Update
Routes Post/update
Sau khi chúng ta đã tạo giao diện cho nó xong rồi thì tiếp đến. Trong thư mục routes
có file index.js
. Các bạn tạo cho mình phương thức POST/update
để khi submit cập nhật dữ liệu thì nó sẽ được gửi lên server và lưu lại trong database nha.
Các hoạt động như thế nào thì mình cũng đã nói ở trên rồi nha.
// Update books
router.post("/update", function (req, res, next) {
var id = req.body.id;
Book.findById(id, function (err, book) {
if (err) { console.log(err) }
book.title = req.body.title
book.description = req.body.description
book.author = req.body.author
book.save()
})
res.redirect('/')
})
Bây giờ các bạn thử sửa lại books sau đó cập nhật lại xem thử nó có thay đổi không nha.
Mình sẽ đổi author là Long Code Rạo thành Long Coder, sau đó mình sẽ submit để xem phần read book và database nó có thay đổi không nha.
Xóa Books
Cách hoạt của xóa books cũng tương tự như cập nhật books cũng sử dụng id để xóa khác ở chỗ một thằng thì cập nhật còn thằng kia là xóa mà thôi.
Giao diện để xóa dữ liệu book
Giao diện thằng này cũng đơn giản thôi chỉ chứa một trường id thôi nhưng các bạn nhớ rằng là action của mỗi thằng phải khác nhau nha.
//- Delete books
div.content_book
h2 Delete Books
form(action="/delete" method="POST")
div
label(for="id") ID
input(type="text" name="id" required="required")
button(type="submit") Delete
Routes Post/delete
Trong phần này các bạn chỉ cần tìm trong database có id nào mà trùng với id mà mình nhập không sau đó xóa thôi.
// Delete books
router.post("/delete", function (req, res, next) {
var id = req.body.id
Book.findByIdAndDelete(id, function (err) {
if (err) { console.log(err) }
})
res.redirect('/')
})
Chúng ta cùng xem thử trong database nó có xóa không nha và phần đọc books có hiển thị dữ liệu của books không nha.
Và đây là kết quả khi chúng ta submit xóa dữ liệu books
và tất cả đều trống trơn trong database cũng bị xóa.
Vậy là chúng ta đã hoàn thành xong các chức năng CRUD, bây giờ chúng ta chỉ cần css lại cho nó nữa là ok.
Viết CSS Cho CRUD Books
Trong file style.css
chúng ta sẽ cho bốn cái chức năng trên cùng một hàng bằng cách sử dụng float và width.
Tiếp đến các bạn chỉnh các màu width cũng như các font sao cho đẹp và phù hợp nhất nha.
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html,
body {
padding: 2rem;
font-family: tahoma;
}
li {
list-style: none;
}
label {
margin-bottom: .2rem;
font-weight: 700;
display: block;
}
.content_book {
width: 25%;
display: block;
float: left;
}
.title-book h1 {
text-align: center;
color: red;
margin-bottom: 1.5rem;
}
.content_book h2 {
color: #006600;
margin-bottom: .5rem;
}
div {
margin-bottom: .5rem;
}
input[type="text"] {
width: 80%;
padding: 5px;
border-radius: 3px;
outline: none;
border: 1px solid #a5a5a5;
}
button {
background: blue;
cursor: pointer;
text-transform: uppercase;
padding: 0.5rem;
border-radius: 5px;
color: #fff;
border: none;
}
.content_book ul {
background: aqua;
padding: 0.5rem;
width: 100%;
margin-bottom: .5rem;
}
.content_book ul li {
text-overflow: ellipsis;
overflow: hidden;
margin-bottom: .3rem;
}
.content_book ul li span {
font-weight: bold;
}
.content_book .list_books{
width: 90%;
height: 220px;
overflow-y: scroll;
}

Các bạn sẽ thấy tại sao lại phải có thanh scroll bởi vì khpi chúng ta tạo rất nhiều books thì số lượng mà render ra rất nhiều và dài làm cho giao diện không được đẹp. Vì thế chúng ta sẽ cho height cố định, overflow-y: scroll.
Khi mà số lượng books vượt qua height cố định thì sẽ xuất hiện scroll.
Reponsive Giao Diện Cho CRUD Books
Trong file layout.pug
các bạn nhớ add thẻ meta này để nó có thể reponsive giao diện nha.
meta(name='viewport', content='width=device-width, initial-scale=1')
Mobile
Trong file style.css
các bạn chỉ cần cho width:100% cho mỗi chức năng là được.
/* Mobile */
@media only screen and (min-width:240px) and (max-width:480px) {
html,
body {
padding: 1rem;
font-family: tahoma;
}
.content_book {
width: 100%;
margin-bottom: 2rem;
}
}
Và đây là giao diện mobile khi chúng ta đã reponsive nha.
Tablet
Thì cũng đơn giản thôi trong file style.css
các bạn chỉ cần cho width:50% cho mỗi chức năng là được.
/* Tablet */
@media (min-width:480px) and (max-width:768px) {
.content_book {
width: 50%;
}
}
Và đây là giao diện tablet khi chúng ta đã reponsive nha.
Vậy là xong rồi nha, các bạn có thể tham khảo code mà mình đã push lên github tại đây nha
Lời Kết
Vậy Là Xong bài Hướng Dẫn Xây Dựng Web CRUD Books Với NodeJS, Express và MongoDB rồi nhé. Mình mong muốn sau bài topic này các bạn có thể nắm vững thêm về NodeJS, Express và MongoDB và có thể tự tay mình làm những project không cần phải quá đặc biệt nhưng nó do chính bạn làm thì cũng coi như là thành quả trong quá trình bạn học được.
Nếu các bạn cảm thấy bài viết của mình hay thì các bạn có thể ủng hộ mình để mình có thêm động lực để ra những bài topic hay và chất lượng hơn ủng hộ mình tại đây nha.
Chúc Các Bạn Thành Công!!