Cơ sở dữ liệu đóng vai trò vô cùng quan trọng trong việc phát triển ứng dụng trên cả nền tảng di động và web. Nắm vững kiến thức về cách ứng dụng tương tác với cơ sở dữ liệu là điều cần thiết để lưu trữ dữ liệu hiệu quả. Bài viết này sẽ hướng dẫn bạn cách sử dụng Sequelize ORM để tối ưu hóa cách thức ứng dụng Node.js của bạn giao tiếp với cơ sở dữ liệu.
ORM là gì? Ưu điểm và các công cụ phổ biến
Ánh xạ quan hệ đối tượng (ORM) là một khái niệm giao tiếp cơ sở dữ liệu trong lập trình, liên quan đến việc trừu tượng hóa các kiểu dữ liệu thành các biến lập trình hướng đối tượng tương thích. Nói một cách đơn giản, nó giúp loại bỏ việc sử dụng các truy vấn và kiểu lưu trữ do cơ sở dữ liệu xác định để dễ dàng tạo cơ sở dữ liệu thông qua các ngôn ngữ lập trình.
Việc sử dụng ORM đã được áp dụng rộng rãi trong lĩnh vực công nghệ vì có nhiều ưu điểm hơn so với các phương pháp truy vấn cơ sở dữ liệu thông thường. Dưới đây là một số trong số đó:
- Giảm thiểu rủi ro thao túng dữ liệu: SQL injection và các kiểu tấn công tương tự liên quan đến việc đưa các cú pháp và truy vấn SQL độc hại vào cơ sở dữ liệu, có thể gây nguy hiểm cho bảo mật cơ sở dữ liệu. Việc có ORM sẽ bổ sung tính năng sơ đồ xác thực đầu vào, đồng thời nêu chi tiết cú pháp biến đầu vào dự kiến và xử lý nó cho phù hợp.
- Dễ dàng giao tiếp với cơ sở dữ liệu: ORM giúp đơn giản hóa việc sử dụng cơ sở dữ liệu như một công cụ dữ liệu mà không cần trải qua quá trình tìm hiểu một ngôn ngữ truy vấn cơ sở dữ liệu khác. Lược đồ ORM có thể được nêu bật theo kiểu hướng đối tượng trong ngôn ngữ ứng dụng và có thể được định cấu hình để tự động dịch mã thành các truy vấn tương thích với cơ sở dữ liệu.
- Khả năng sử dụng lại và tính linh hoạt cao: Tính năng này cũng cho phép dễ dàng chuyển đổi mã, duy trì một cơ sở mã tích hợp cơ sở dữ liệu duy nhất trong khi thay đổi cơ sở dữ liệu mà không có kết quả bất lợi nào. Nó có tính linh hoạt cao và có thể được sử dụng trong bất kỳ cơ sở dữ liệu nào bạn chọn.
- Tích hợp các tính năng bổ trợ: ORM cũng có các tính năng bổ sung được bao gồm để cho phép tương tác với cơ sở dữ liệu. Các tính năng di chuyển cơ sở dữ liệu và quy trình kiểm soát phiên bản cũng được cung cấp.
Với những lợi ích đã nêu trên, dưới đây là một số công cụ ORM phổ biến được sử dụng trên toàn cầu:
- SQLAlchemy
- Prisma ORM
- Sequelize
- ActiveRecord
- TypeORM
- Waterline
Đối với bài viết này, chúng ta sẽ sắp xếp hợp lý các trường hợp sử dụng ORM của mình thành một dự án Node.js cơ bản được liên kết với cơ sở dữ liệu MySQL. Chúng ta sẽ sử dụng Sequelize ORM làm công cụ lựa chọn.
Với mức tải xuống gói trung bình 8,5 triệu lượt mỗi tháng và một cộng đồng phát triển tích cực, Sequelize tự hào có các tính năng mạnh mẽ tích hợp liền mạch cơ sở dữ liệu với các ứng dụng backend. Nó cũng cung cấp tài liệu hướng người dùng giúp hướng dẫn người dùng thiết lập và sử dụng công cụ.
Nó cũng cung cấp hỗ trợ cho MySQL, DB2 và SQLite Microsoft SQL Server và cung cấp các tính năng như sao chép đọc, tải chậm và các thuộc tính giao dịch cơ sở dữ liệu hiệu quả.
Tiếp theo, chúng ta sẽ thiết lập ứng dụng web của mình và cài đặt Sequelize để kết nối chúng ta với cơ sở dữ liệu MySQL được lưu trữ cục bộ.
Cách thiết lập máy chủ Node.js của bạn
Trong phần này, bạn sẽ thiết lập máy chủ Node của chúng ta. Điều hướng đến dòng lệnh và thực thi npm init. Lệnh này tạo một cấu trúc dự án Node mới cho bạn.
Tiếp theo, cài đặt gói Express - gói này sẽ đóng vai trò là framework backend. Bạn có thể thực hiện việc này bằng cách chạy lệnh npm i express.
Cách tích hợp các gói có liên quan
Đối với mục đích của hướng dẫn này, chúng ta sẽ cài đặt trình quản lý gói Node Sequelize trong ứng dụng Node của chúng ta để thiết lập giao tiếp ORM với cơ sở dữ liệu.
Để thiết lập điều này, hãy thực thi npm i sequelize.
Chúng ta sẽ sử dụng cơ sở dữ liệu MySQL được lưu trữ cục bộ. Để thực hiện việc này, chúng ta sẽ cài đặt trình điều khiển cơ sở dữ liệu gói npm. Trong trường hợp này, chúng ta sẽ cài đặt mysql2.
Chạy npm i mysql2 để cài đặt nó.
Hãy chuyển sang định cấu hình kết nối với cơ sở dữ liệu và xây dựng dự án demo của chúng ta.
Dự án demo
Trong phần này, chúng ta sẽ xây dựng một máy chủ backend đơn giản thực hiện các thao tác Tạo-Đọc-Cập nhật-Xóa (CRUD), với thư viện Sequelize đóng vai trò là đường ống kết nối.
Để bắt đầu dự án, chúng ta sẽ phải thiết lập kết nối cơ sở dữ liệu cho ứng dụng của mình. Chúng ta sẽ tạo một tệp kết nối cơ sở dữ liệu và thiết lập thông tin đăng nhập cơ sở dữ liệu của chúng ta. Bạn có thể đặt tên cho tệp là SequelizeConfig.
module.exports = { HOST: "localhost", USER: "root", PASSWORD: "", DB: "sequel", dialect: "mysql" }
Trong đoạn mã trên, thông tin đăng nhập cơ sở dữ liệu đã được chỉ định, cùng với địa chỉ máy chủ. Trong trường hợp của chúng ta, cơ sở dữ liệu được lưu trữ cục bộ, vì vậy localhost là máy chủ mặc định.
Thông tin đăng nhập cơ sở dữ liệu cũng được cung cấp. Người dùng ở đây là root, trong khi mật khẩu được đặt thành một chuỗi rỗng. Điều này nên được điều chỉnh để đảm bảo bảo mật cơ sở dữ liệu. Tôi cũng đã tạo một cơ sở dữ liệu không hoạt động có tên là “sequel”.
Dialect đề cập đến loại cơ sở dữ liệu mà người dùng dự định sử dụng. Trong trường hợp của chúng ta, dialect là MySQL. Lưu ý rằng điều này cũng có thể được sao chép trên cơ sở dữ liệu được lưu trữ trên đám mây với thông tin đăng nhập đã được lấy. Với điều đó, hãy tích hợp tệp kết nối với ứng dụng.
const SequelConfig = require('../config/sequelize'); const Sequelize = require('sequelize'); const sequelize = new Sequelize(SequelCOnfig.DB, SequelCOnfig.USER, SequelCOnfig.PASSWORD, { host: SequelCOnfig.HOST, dialect: SequelCOnfig.dialect });
Để tạo điều kiện kết nối với cơ sở dữ liệu, các biến trong tệp cấu hình đã được nhập và khởi tạo trong tệp thiết lập Sequelize.
const db = {}; db.Sequelize = Sequelize; db.sequelize = sequelize; db.user = require('../model/user.model')(sequelize, Sequelize); db.token = require('../model/token.model')(sequelize, Sequelize) module.exports= db;
Tệp trên nhập tệp config được tạo trước đó và khởi tạo thư viện Sequelize. Sau đó, mã sẽ tìm nạp chi tiết cơ sở dữ liệu được nhập trong tệp cấu hình và khi được thực thi, hãy tạo cơ sở dữ liệu.
Hơn nữa, các mô hình cơ sở dữ liệu khác nhau sẽ được thảo luận sau đó được tích hợp với cơ sở dữ liệu không hoạt động và tạo bảng cơ sở dữ liệu SQL.
Để khởi động và chạy, tệp cơ sở dữ liệu được tạo được gọi bằng phương thức sequelize.sync(). Bất kỳ lỗi nào gặp phải sẽ được ghi lại và kết nối cơ sở dữ liệu sẽ bị chấm dứt.
db.sequelize.sync().then(() => { console.log('user created '); }).catch(err => { console.error(err) })
Chúng ta sẽ tiếp tục thảo luận về các mô hình cơ sở dữ liệu.
Các mô hình dữ liệu trong Sequelize ORM
const Sequelize = require("sequelize"); module.exports = (sequelize) => { sequelize.define( "user", { firstName: { type : Sequelize.DataTypes.STRING, allowNull : false }, lastName: { type : Sequelize.DataTypes.STRING, allowNull : false }, email : { type : Sequelize.DataTypes.STRING, allowNull : false, unique: true }, password: { type : Sequelize.DataTypes.STRING, allowNull : false }, role: { type : Sequelize.DataTypes.STRING, allowNull : false } } ) }
Trong đoạn mã trên, mô hình người dùng đã được khởi tạo trong Sequelize ORM và các chi tiết trường đã được chỉ định: email, role, lastName và password. Loại dữ liệu sẽ nhận cũng được chỉ định.
Nó cũng cung cấp một tùy chọn để đảm bảo tính duy nhất của chi tiết người dùng và tùy chọn để ngăn người dùng bỏ trống một số trường thông qua việc sử dụng allowNull = false.
Khi thực thi ứng dụng, Sequelize ORM tạo một SQL tương đương với mô hình dưới dạng bảng dữ liệu.
Tiếp theo, chúng ta sẽ làm việc trên các hàm CRUD trong Node.js.
Thao tác tạo (Create Operation)
const createUser = async (userInfo) => { try { // Check if the email already exists in the database const ifEmailExists = await User.findOne({ where: { email: userInfo.email } }); if (ifEmailExists) { throw new ApiError('Email has already been registered'); } // Create the new user const newUser = await User.create(userInfo); return newUser; // Return the created user object } catch (error) { // Handle errors such as validation or uniqueness constraint throw error; } };
Hàm trên làm nổi bật hàm bộ điều khiển để tạo mục nhập người dùng trong máy chủ Express.
Hàm này là không đồng bộ, cho phép thực thi một số lệnh trước khi thực thi cuối cùng. Mã đảm bảo rằng email người dùng không tồn tại trong cơ sở dữ liệu trước khi tạo người dùng mới.
Ngoài ra, chúng tôi cũng đảm bảo rằng mỗi trường email là duy nhất. Nếu chi tiết người dùng được nhập vào cơ sở dữ liệu thành công, một phản hồi “thành công” sẽ được gửi lại cho máy chủ. Ngoài ra, bất kỳ lỗi nào gặp phải sẽ dẫn đến việc chấm dứt hàm và lỗi được gửi lại cho máy chủ.
Thao tác đọc (Read Operation)
const FetchUser = async (userId) => { let userDets; if (userId) { // Fetch a single user by ID if userId is provided userDets = await User.findOne({ where: { id: userId } }); // Check if the user exists if (!userDets) { throw new ApiError(httpStatus.NOT_FOUND, 'User not found'); } } else { // Fetch all users if no userId is provided userDets = await User.findAll(); // Check if any users were found if (userDets.length === 0) { throw new ApiError(httpStatus.NOT_FOUND, 'No users found'); } }
Thao tác đọc tìm nạp truy vấn mong muốn và gửi lại cho người dùng mà không sửa đổi. ID người dùng, phải là duy nhất, được sử dụng để tìm kiếm một người dùng cụ thể. Trong trường hợp này, chúng tôi muốn truy cập vào tất cả người dùng được tạo trong cơ sở dữ liệu.
Trong trường hợp không tìm thấy truy vấn được yêu cầu, mã lỗi thích hợp sẽ được tạo.
Thao tác cập nhật (Update Operation)
const updateUser = async (userId, userDetails) => { // First, find the user by their ID const user = await User.findOne({ where: { id: userId } }); if (!user) { throw new ApiError(httpStatus.BAD_REQUEST, "User doesn't exist"); } // Update the user with the new details await User.update(userDetails, { where: { id: userId } }); // Fetch the updated user to return it const updatedUser = await User.findOne({ where: { id: userId } }); console.log('Updated user:', updatedUser); // Log the updated user return updatedUser; // Return the updated user object };
Thao tác cập nhật nhằm mục đích sửa đổi dữ liệu được nhập trong các thao tác trước đó. Đó là, để cập nhật một số trường dữ liệu.
Trong trường hợp của Sequelize, phương thức update được gọi. Để thành công với điều này, người dùng cụ thể cần được chỉnh sửa phải được xác định. Đoạn mã trên sau đó tạo trường dữ liệu được cập nhật và gửi nó dưới dạng đầu ra của yêu cầu thành công.
Thao tác xóa (Delete Operation)
const deleteUser = async (userId) => { const user = await User.findOne({ where: { id: userId } }); if (!user) { throw new ApiError(httpStatus.BAD_REQUEST, "User doesn't exist"); } // Delete the user await user.destroy(); console.log('Deleted user:', user); // Log the deleted user return user; // Return the deleted user object (useful for confirmation) };
Thao tác xóa được gọi khi dữ liệu trong bảng cơ sở dữ liệu cần được xóa. Sequelize tạo điều kiện cho việc này thông qua việc sử dụng phương thức destroy. Phương pháp này xóa một người dùng cụ thể. Khi được thực thi, mã phản hồi thành công được hiển thị.
Thông tin bổ sung
Cho đến nay, chúng ta đã tích hợp thư viện ORM để đóng vai trò là kết nối giữa ứng dụng backend của chúng ta và cơ sở dữ liệu quan hệ của chúng ta. Chúng ta cũng đã khám phá các khái niệm nâng cao như di chuyển cơ sở dữ liệu và các hoạt động CRUD. Để tìm hiểu thêm về điều này, bạn có thể khám phá tài liệu và sử dụng nó trong việc xây dựng các dự án phức tạp hơn, vì việc học thực hành được khuyến khích nhiều hơn.
Cảm ơn các bạn đã theo dõi.