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

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, descriptionauthor.

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:&nbsp
           =book.title
          li
           span Description:&nbsp
           =book.description
          li
           span Author:&nbsp
           =book.author
          li
           span ID:&nbsp
           =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 bookdatabase 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!!