Tại sao lại chọn Fastify framework thay vì ExpressJS?

2977

Trước đây, mỗi khi bắt đầu một dự án Nodejs mới là mình mặc định sử dụng ExpressJS framework. Có lẽ mọi người ở đây chắc cũng giống mình.

Với những ưu điểm, thế mạnh về cộng đồng, ExpressJS vẫn luôn là framework được sử dụng rộng rãi nhất hiện nay.

Tuy nhiên, cho đến một ngày, mình nhận dự án với code base đang sử dụng fastify. Mình khá tò mò về nodejs framework mới mẻ này. Ok, thử lên mạng tìm hiểu xem sao, kết quả mình thấy là rất nhiều người cũng sử dụng fastify cho các dự án của họ. Đặc biệt là fastify được đánh giá rất tốt.

Sau một thời gian làm việc với fastify, mình ngày càng cảm thấy yêu thích nó hơn và cũng dần sử dụng nó cho các dự án sau này.

Bài viết này, chúng ta cùng nhau tìm hiểu fastify là gì? Tại sao lại chọn fastify cho các dự án Nodejs nhé.

Giới thiệu Fastify

Fastify không chỉ là một nodejs framework khác bên cạnh ExpressJS. Nó là một trong những framework được quảng bá là nhanh nhất hiện nay. Cái này có lẽ phải để các dự án thực tế trả lời, chứ nghe mấy ông sáng lập quảng cáo thì cũng biết vậy thôi nhỉ

Có một số lý do để bạn thích fastify:

  • Hiệu năng cao, tốc độ nhanh
  • Hỗ trợ Async/Await cho tất cả các thành phần như: route handlers, application hooks, server methods (ví dụ: .listen())…
  • JSON input: Mặc định fastify hỗ trợ đọc json từ body mà bạn không cần phải cấu hình chỉ định application/json
  • Logging: Fastify có sẵn một trình ghi log. Do đó, bạn không cần phải suy nghĩ cách ghi log như nào, lưu log ở đâu… Fastify nó làm sẵn cho hết rồi.

Và còn rất nhiều ưu điểm khác của fastify so với ExpressJS, các bạn cứ sử dụng và trải nghiệm dần dần.

  Monstache là gì? Đồng bộ mongodb sang Elasticsearch với Monstache

  Tìm hiểu về ExpressJS và những ưu nhược điểm của ExpressJS

Xây dựng ứng dụng quản lý sách bằng fastify + MongoDB

Để các bạn có một trải nghiệm, một cái nhìn chân thực hơn về fastify framework, chúng ta sẽ cùng nhau thực hành tạo một ứng dụng REST API.

Tổng quan dự án: Dự án xây dựng REST API với đầy đủ các tính năng CRUD một cửa hàng sách. Để lưu trữ dữ liệu, chúng ta sử dụng MongoDB.

Các dependencies sử dụng trong dự án:

  • fastify: Tất nhiên rồi, dự án này chúng ta đang học cách sử dụng fastify mà.
  • mongoose: module giúp đơn giản hóa thao tác với MongoDB.
  • nodemon: giúp tự khởi động server mỗi khi cập nhật code.
  • boom: Hỗ trợ xử lý HTTP error đơn giản hơn.

Ok, mọi thứ đã sẵn sàng, chúng ta vào việc thôi!

Khởi tạo dự án fastify

Để tạo một dự án một dự án mới tinh, mình hay dùng câu lệnh sau:

Trên Linux:
mkdir fastify-store && cd fastify-store && touch index.js && npm init -y

Trên Window:
mkdir fastify-store && cd fastify-store && type nul > index.js && npm init -y

Trong đó, fastify-store là tên thư mục dự án.

Tiếp theo là cài đặt các dependencies cần thiết mà mình đã liệt kê ở trên:

npm install boom mongoose fastify nodemon

Sau khi cài đặt xong, chúng ta mở index.js và thêm đoạn code để tạo một http server.

//index.js
const fastify = require('fastify')({
  logger: true
})
const PORT = 3000

fastify.get('/', async() => {
  return {
      Test: 'VNTALKING xin chào cả nhà'
  }
})

const serve = async () => {
  try {
      await fastify.listen(PORT)
      fastify.log.info(`Server listening to PORT ${fastify.server.address().port}`)
  } catch (err) {
      fastify.log.error(err)
      process.exit(1)
  }
}
serve()

Trong đoạn code trên:

  • Bật fastify logger, mặc định tính năng này là disabled.
  • Chọn một port và gán cho ứng dụng. Cụ thể, port mình chọn ở trên là 3000
  • Định nghĩa một route (“/”). Tức là trang root của chúng ta.

Sau khi sửa code xong thì chạy server thôi.

npx nodemon index

Kết quả khi chạy lệnh trên:

fastify-hello-world

Mở postman (hoặc trình duyệt) để truy cập thử đường dẫn http://localhost:3000

fastify-hello-world-test

Xem thêm việc làm Node.js developer hấp dẫn nhất tại TopDev

Cài đặt MongoDB

Các bạn tải MongoDB về và cài đặt trên máy tính của mình nhé. Bạn cài đặt bình thường như bao phần mềm khác thôi. Do khuôn khổ bài viết, mình sẽ không hướng dẫn chi tiết cách cài đặt MongoDB.

install-mongodb

Lưu ý: Việc cài đặt mongodb trên máy tình dùng cho dự án để học tập thì cơ bản như vậy. Nhưng đối với dự án product thật, bạn sẽ cần phải cấu hình bảo mật như authentication, firewall,v.v…

Sau khi cài đặt mongodb thành công, bạn sử dụng công cụ Robo3T để kết nối vào DB.

connect-mongodb-local

Tạo mới database. Trong dự án này thì mình đặt tên là fastify-store

create-database-mongodb

Ngon rồi đấy!

Quay trở lại dự án, bạn mở index.js và thêm đoạn code kết nối tới DB.

const mongoose = require('mongoose')
//setup DB
const mongoUrl ="mongodb://127.0.0.1:27017/fastify-store"
try {
    mongoose.connect(mongoUrl, {
        useNewUrlParser: true,
        useUnifiedTopology: true
    })
    console.log("Database connected sucessfully");
} catch (error) {
    console.log(error);
}

Lưu code lại và nhìn thấy dòng “Kết nối tới Database thành công” trong màn hình console là được.

Tạo Book Model

Model là một class giúp chúng ta tương tác với database. Một instance của modal là một document.

Bạn tạo mới một file đặt tên là Book.js

// Book.js
const mongoose = require('mongoose')
const bookSchema = new mongoose.Schema({
   title:{
       type: String,
       required: true
   },
   description:{
       type: String,
       required: true
   },
   genre: {
       type: String,
       required: true
   },
   author: {
       type: String,
   },
   price: {
       type: Number,
       required: true
   },
   completed:{
       type: Boolean,
       required: true
   }
});

module.exports = mongoose.model('Book', bookSchema)

Mongoose sẽ tự động convert model thành một document trong Database.

Lưu ý: Khi tạo mới collection, mongoose sẽ tự thêm ký tự “s” vào tên collection mà bạn đặt. Ví dụ: như trong đoạn code trên, bạn đặt tên collection là “Book” thì trong database nó sẽ có tên tương ứng là books.

Khởi tạo Routes và Controllers

Tương ứng với các tính năng CRUD, cụ thể: Xem, Thêm, sửa, xóa. Chúng ta sẽ định nghĩa các endpoint như sau:

  • GET, url: /api/books – Trả về danh sách toàn bộ sách trong cơ sở dữ liệu
  • GET, url: /api/book/:id – Trả về một sách tương ứng với id truyền vào
  • POST, url:/api/book – Thêm một sách mới
  • PUT, url: /api/book/:id – Cập nhật sách theo id
  • DELETE, url: /api/book/:id – Xóa một sách theo id

Chúng ta tạo mới một file router đặt tên là bookRoutes.js

// bookRoutes.js
const bookController = require('./bookController');
const routes = [
    {
        method: 'GET',
        url: '/api/books',
        handler: bookController.getAllBooks
    },
    {
        method: 'GET',
        url: '/api/book/:id',
        handler: bookController.getSingleBook
    },
    {
        method: 'POST',
        url: '/api/book',
        handler: bookController.addNewBook
    },
    {
        method: 'PUT',
        url: '/api/book/:id',
        handler: bookController.updateBook
     },
     {
        method: 'DELETE',
        url: '/api/book/:id',
        handler: bookController.deleteBook
     }
]
module.exports = routes

Tương ứng với router là controller: bookController.js

// bookController.js
const boom = require('boom')
const Book = require('./Book');
// get all books
exports.getAllBooks = async (req, reply) => {
    try {
        let books = await Book.find()
        return reply.code(200)
        .send(
            {
             Message: "Success",
             data: books
            }
         )
     } catch (err) {
         throw boom.boomify(err)
     }
}
//get a single book by id
exports.getSingleBook = async (req, reply) => {
    try {
        const id = req.params.id
        let book = await Book.findById(id)
        return reply.code(200)
        .send({ Message: "Success", data: book}, )
    } catch (err) {
        throw boom.boomify(err)
    }
}
//add a new book
exports.addNewBook = async (req, reply) => {
    try {
        let book = new Book(req.body);
        let newBook = await book.save()
        return reply.code(200)
        .send({ Message: "New Book added successfully", data: newBook})
    }
    catch (err) {
        throw boom.boomify(err)
    }
}
//edit a book
exports.updateBook = async (req, reply) => {
    try {
        const id = req.params.id
        let updatedBook = await Book.findByIdAndUpdate(id, req.body, {
            new: true
        })
        return reply.code(200)
        .send({ Message: "Book updated successfully", data: updatedBook});
    } catch (err) {
        throw boom.boomify(err)
    }
}
//delete a book
exports.deleteBook = async (req, reply) => {
    try {
        const id = req.params.id
        let deletedBook = await Book.findByIdAndDelete(id);
        return reply.code(200)
        .send({ Message: `${deletedBook.title} has been deleted successfully`, data: id})
    } catch (err) {
        throw boom.boomify(err)
    }
}

Cuối cùng là thêm khai báo router trong index.js

...
const routes = require("./bookRoutes")
...
routes.forEach((route, index) => {
    fastify.route(route)
});
...

Server sẽ tự khởi động lại và chúng ta tiến hành test thôi nhỉ!

Test ứng dụng fastify với Postman

Đầu tiên là tạo mới và lưu một cuốn sách:

add-new-book-api-fastify

Xem danh sách toàn bộ sách trong database

get-all-book-api-fastify

Các bạn có thể tải toàn bộ mã nguồn minh họa trong bài viết tại đây:

Tạm kết

Qua bài viết này, chúng ta đã biết tới một fastify framework vô cùng mạnh mẽ, đặc biệt trong các dự án xây dựng REST API.

Fastify framework xứng đáng là một ứng cử viên nặng ký cho các dự án sắp tới của bạn. Mình tin là như vậy

Bạn đã sử dụng fastify cho dự án nào chưa? Đừng tiếc một bình luận bên dưới nhé!

Bài viết gốc được đăng tải tại vntalking.com

Có thể bạn quan tâm:

Xem thêm Việc làm IT hấp dẫn tại TopDev