Chuyên mục

Nodejs

Nodejs tutolrial

147 bài viết
JWT: Hộ Chiếu Điện Tử Cho Dân Lập Trình Node.js
25/03/2026

JWT: Hộ Chiếu Điện Tử Cho Dân Lập Trình Node.js

Chào các "thần đồng" Gen Z! Hôm nay, anh Creyt sẽ "khai sáng" cho các em một khái niệm cực kỳ "hot" và "cool" trong giới lập trình web hiện đại: JWT authentication. JWT là gì? "Hộ Chiếu Điện Tử" của bạn Thôi bỏ cái mớ lý thuyết khô khan đi, anh em mình nói chuyện thực tế nhé. Tưởng tượng thế này: Ngày xưa, mỗi lần bạn vào bar xịn, bạn phải xuất trình CCCD/CMND cho bảo vệ. Rồi mỗi lần vào lại, lại phải xuất trình. Mệt không? Quá mệt! Server của chúng ta cũng vậy, mỗi lần bạn gửi yêu cầu lên, nó lại phải hỏi: "Ê, mày là ai? Mày có quyền vào đây không?" và phải đi dò trong cái "sổ đen" (database session) xem bạn đã đăng nhập chưa. JWT (viết tắt của JSON Web Token) chính là cái "thẻ thành viên VIP" của bạn đó. Lần đầu bạn đăng nhập (như đăng ký thẻ VIP), hệ thống sẽ cấp cho bạn một cái thẻ đặc biệt. Từ đó về sau, mỗi lần bạn muốn vào cửa (gửi request lên server), bạn chỉ cần "quẹt thẻ" này là xong. Bảo vệ (server) chỉ cần nhìn cái thẻ, biết nó hợp lệ, biết bạn là ai và có quyền gì, là cho qua ngay, không cần phải chạy vào phòng lưu trữ lục lọi "sổ đen" nữa. Cái hay của JWT là gì? Server không cần nhớ mặt bạn, không cần lưu bất kỳ thông tin session nào về bạn. Nó chỉ cần tin vào cái "chữ ký" trên cái thẻ VIP của bạn thôi. "Stateless" là vậy đó, server nhẹ gánh, dễ dàng mở rộng như diều gặp gió. Cấu tạo của một "Thẻ VIP" JWT Một cái JWT nhìn thì phức tạp, nhưng thực ra nó chỉ có 3 phần chính, được ngăn cách bởi dấu chấm (.): Header (Phần đầu): Giống như cái bìa của cuốn hộ chiếu, ghi loại tài liệu là gì (JWT) và dùng thuật toán mã hóa nào để bảo vệ nó (ví dụ: HS256 - HMAC SHA256). Nó trông như này khi giải mã base64: { "alg": "HS256", "typ": "JWT" } Payload (Phần thân): Đây là "nội dung" chính của cái thẻ VIP, chứa các thông tin về bạn (gọi là "claims"). Ví dụ: user_id, username, roles của bạn, và quan trọng nhất là exp (expiration time) - thời gian hết hạn của cái thẻ. Lưu ý cực mạnh: Đừng bao giờ nhét thông tin nhạy cảm như mật khẩu, số thẻ tín dụng vào đây nhé! Payload chỉ là base64 encoded, ai cũng có thể giải mã để đọc được. Nó trông như này: { "user_id": "12345", "username": "creyt_dev", "role": "admin", "exp": 1678886400 // Thời gian hết hạn (Unix timestamp) } Signature (Chữ ký): Đây là phần quan trọng nhất, giống như con dấu niêm phong của chính phủ trên hộ chiếu, đảm bảo cái thẻ VIP này là "hàng thật", không bị làm giả hoặc chỉnh sửa. Nó được tạo ra bằng cách lấy Header + Payload + một "bí mật nhà nghề" (Secret Key) mà chỉ server biết, rồi dùng thuật toán mã hóa. Nếu ai đó cố gắng thay đổi Header hoặc Payload, chữ ký sẽ không khớp, và server sẽ biết ngay đây là hàng "fake". HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) Khi ba phần này kết hợp lại, bạn sẽ có một chuỗi dài loằng ngoằng như này: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDUiLCJ1c2VybmFtZSI6ImNyZXl0X2RldiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTY3ODg4NjQwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c Code Ví Dụ Minh Hoạ (Node.js): "Triển" ngay thôi! Giờ thì chúng ta sẽ "bắt tay vào làm" một hệ thống JWT authentication đơn giản với Node.js và Express. Để bắt đầu, bạn cần cài đặt các gói sau: npm init -y npm install express jsonwebtoken dotenv Tiếp theo, tạo file .env để lưu trữ JWT_SECRET của bạn. Đừng bao giờ commit file này lên Git nhé! JWT_SECRET=daylamotbibatkhongaitheobiet_anhcreyt Bây giờ là code chính trong server.js: require('dotenv').config(); // Load biến môi trường từ .env const express = require('express'); const jwt = require('jsonwebtoken'); const app = express(); const PORT = process.env.PORT || 3000; app.use(express.json()); // Kích hoạt middleware để đọc JSON từ request body const JWT_SECRET = process.env.JWT_SECRET; // Middleware để xác thực JWT function verifyToken(req, res, next) { const authHeader = req.headers['authorization']; // Format của header: 'Bearer TOKEN' const token = authHeader && authHeader.split(' ')[1]; if (token == null) { return res.status(401).json({ message: 'Không có token, truy cập bị từ chối' }); } jwt.verify(token, JWT_SECRET, (err, user) => { if (err) { return res.status(403).json({ message: 'Token không hợp lệ hoặc đã hết hạn' }); } req.user = user; // Gán thông tin người dùng vào request next(); // Chuyển sang middleware hoặc route tiếp theo }); } // 1. Route Đăng nhập (Tạo JWT) app.post('/login', (req, res) => { // Đây là phần giả lập việc kiểm tra username/password từ database const { username, password } = req.body; if (username === 'creyt' && password === '123456') { // Nếu đăng nhập thành công, tạo payload cho JWT const user = { id: '123', username: username, role: 'admin' }; // Ký JWT với secret key và thời gian hết hạn const accessToken = jwt.sign(user, JWT_SECRET, { expiresIn: '1h' }); // Hết hạn sau 1 giờ res.json({ accessToken: accessToken }); } else { res.status(401).json({ message: 'Sai tên đăng nhập hoặc mật khẩu' }); } }); // 2. Route được bảo vệ (Chỉ truy cập khi có JWT hợp lệ) app.get('/profile', verifyToken, (req, res) => { // req.user đã có thông tin người dùng từ JWT sau khi verifyToken chạy res.json({ message: `Chào mừng ${req.user.username} đến với trang cá nhân của bạn!`, user: req.user }); }); // 3. Một route khác chỉ dành cho admin (ví dụ) app.get('/admin-dashboard', verifyToken, (req, res) => { if (req.user.role !== 'admin') { return res.status(403).json({ message: 'Bạn không có quyền truy cập trang này!' }); } res.json({ message: `Chào admin ${req.user.username}, đây là bảng điều khiển quản trị!` }); }); app.listen(PORT, () => { console.log(`Server Creyt đang chạy tại http://localhost:${PORT}`); }); Cách "test" code này: Đăng nhập: Dùng Postman hoặc Insomnia, gửi request POST tới http://localhost:3000/login với body là JSON: { "username": "creyt", "password": "123456" } Bạn sẽ nhận được một accessToken. Truy cập route bảo vệ: Gửi request GET tới http://localhost:3000/profile. Trong phần Headers, thêm Authorization: Bearer <accessToken_mà_bạn_nhận_được>. Thử không gửi Authorization header hoặc gửi token sai, bạn sẽ thấy lỗi 401 hoặc 403. Mẹo Vặt (Best Practices) từ "lão làng" Creyt Để sử dụng JWT "ngon lành cành đào" và an toàn, nhớ vài "mẹo" sau: Bảo vệ JWT_SECRET: Đây là "bí mật nhà nghề" của bạn, là chìa khóa để ký và xác minh token. Giữ nó an toàn như giữ bồ cũ của anh Creyt vậy. Tuyệt đối không để lộ, không commit vào Git. Luôn dùng biến môi trường (như process.env.JWT_SECRET) và đảm bảo nó đủ mạnh (dài, phức tạp). Thời hạn token (exp): Đừng cấp thẻ VIP vĩnh viễn! Token nên có thời gian hết hạn ngắn (vài phút đến vài giờ). Điều này giảm thiểu rủi ro nếu token bị đánh cắp. Khi hết hạn, người dùng phải đăng nhập lại hoặc dùng Refresh Token. Refresh Tokens: Khi Access Token (cái JWT ngắn hạn) hết hạn, người dùng sẽ cần một token mới. Thay vì bắt họ đăng nhập lại, bạn có thể cấp một Refresh Token (thường là một chuỗi dài, lưu trong database và có thời gian sống dài hơn). Khi Access Token hết hạn, client dùng Refresh Token để yêu cầu một Access Token mới mà không cần nhập lại mật khẩu. Refresh Token nên được lưu trữ an toàn hơn (ví dụ: HttpOnly cookie) và kiểm tra khi sử dụng. Không nhét thông tin nhạy cảm vào Payload: Nhắc lại lần nữa, Payload chỉ là Base64 encoded, ai cũng có thể giải mã và đọc được. Chỉ đưa vào những thông tin cần thiết để xác định người dùng và quyền hạn của họ thôi. Luôn dùng HTTPS: JWT sẽ được gửi qua mạng trong mỗi request. Nếu không dùng HTTPS, kẻ xấu có thể chặn và đánh cắp token của bạn. HTTPS mã hóa đường truyền, bảo vệ token khỏi bị đọc trộm. Lưu trữ token phía client: Local Storage: Dễ dùng, nhưng dễ bị tấn công XSS (Cross-Site Scripting) nếu website của bạn có lỗ hổng. HttpOnly Cookies: An toàn hơn với XSS vì JavaScript không thể truy cập cookie này. Tuy nhiên, vẫn có thể bị tấn công CSRF (Cross-Site Request Forgery) nếu không có cơ chế bảo vệ phù hợp (như SameSite=Lax/Strict hoặc CSRF tokens riêng). Ứng dụng thực tế: "Thẻ VIP" này dùng ở đâu? JWT được sử dụng rộng rãi trong các hệ thống hiện đại, đặc biệt là: Single Page Applications (SPAs): Các ứng dụng React, Angular, Vue.js thường dùng JWT để xác thực người dùng sau khi họ đăng nhập. Mobile Apps: Các ứng dụng di động như Facebook, Instagram, TikTok thường dùng JWT để giao tiếp với API backend. Microservices Architectures: Khi bạn có nhiều dịch vụ nhỏ giao tiếp với nhau, JWT là một cách tuyệt vời để xác thực và ủy quyền giữa các dịch vụ mà không cần một server xác thực trung tâm phức tạp. APIs công cộng: Nhiều API cung cấp JWT để các ứng dụng bên thứ ba có thể truy cập tài nguyên của họ. Khi nào nên dùng và khi nào nên "né"? Anh Creyt đã từng "thử nghiệm" rất nhiều phương pháp xác thực và rút ra kinh nghiệm xương máu: Nên dùng JWT khi: Bạn cần một hệ thống Stateless: Server không cần lưu trữ session, giúp hệ thống dễ dàng mở rộng theo chiều ngang (scale horizontally). Làm việc với Mobile Apps hoặc SPAs: JWT là lựa chọn tự nhiên cho các ứng dụng client-side không có khái niệm session truyền thống. Hệ thống Microservices: JWT giúp xác thực người dùng trên nhiều dịch vụ khác nhau một cách hiệu quả. Bạn cần giải pháp xác thực đơn giản và hiệu quả: JWT dễ triển khai và hiểu. Cần cân nhắc hoặc "né" JWT khi: Bạn cần khả năng thu hồi token ngay lập tức (Blacklisting): JWT theo bản chất là stateless. Một khi đã ký, token đó vẫn hợp lệ cho đến khi hết hạn. Nếu bạn muốn "đuổi" người dùng ra ngay lập tức (ví dụ: đổi mật khẩu, bị hack), việc thu hồi JWT "đang bay" là khó khăn. Bạn sẽ cần triển khai một cơ chế blacklist riêng (lưu các token đã bị thu hồi vào database hoặc cache) hoặc sử dụng database session truyền thống. Bạn cần lưu trữ nhiều thông tin session trên server: Nếu ứng dụng của bạn yêu cầu server phải lưu trữ nhiều trạng thái người dùng (ví dụ: giỏ hàng chưa thanh toán, cài đặt cá nhân tạm thời), thì session-based authentication có thể phù hợp hơn. Đó, anh Creyt đã "bóc tách" JWT authentication một cách "tận tình" nhất rồi đó. Giờ thì các em đã có thêm một "vũ khí" cực mạnh để "chinh chiến" trong thế giới lập trình web rồi. Nhớ thực hành và áp dụng những mẹo anh Creyt đã chỉ nhé! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

91 Đọc tiếp
Session Management: Giữ Server 'Nhớ' Bạn Là Ai?
23/03/2026

Session Management: Giữ Server 'Nhớ' Bạn Là Ai?

Chào các coder Gen Z! Hôm nay, anh Creyt sẽ dẫn mấy đứa đi khám phá một cái 'bí kíp' cực kỳ quan trọng trong thế giới web, mà nếu thiếu nó, server của tụi mình sẽ... 'mất trí nhớ' ngay lập tức. Cái tên nghe thì hàn lâm nhưng thực ra nó gần gũi như hơi thở của một ứng dụng web: 'Session Management' hay 'Quản lý phiên làm việc'. Session Management là gì? (Kiểu Gen Z) Tưởng tượng thế này, mấy đứa đi vào một quán cà phê sang chảnh. Mấy đứa gọi món, chọn bàn, rồi đi đâu đó một lát, quay lại thì nhân viên vẫn biết mấy đứa là ai, món gì đang đợi, bàn nào đang ngồi. Đó là vì quán có một 'hệ thống' để 'nhớ' mấy đứa. Trong thế giới web cũng vậy. Mỗi khi mấy đứa truy cập một trang web, mỗi hành động của mấy đứa (đăng nhập, thêm hàng vào giỏ, xem trang cá nhân) là một 'lần tương tác'. 'Session Management' chính là cái 'chứng minh thư' mà server cấp cho mấy đứa, giúp server 'nhớ mặt đặt tên', biết rõ 'thằng cu/con bé này' đang làm gì, đã làm gì, và cần gì. Nó như một 'thư ký riêng' của mỗi user trên server vậy. Tại sao lại cần Session Management? Vấn đề nằm ở chỗ, cái giao thức HTTP mà web dùng ấy, nó 'vô cảm' lắm. Nó 'stateless', tức là mỗi khi mấy đứa gửi một yêu cầu (request) lên server, server coi đó là một yêu cầu hoàn toàn mới, độc lập với các yêu cầu trước đó. Nó không 'nhớ' gì cả. Cứ như mỗi lần mấy đứa nói chuyện với server là y như lần đầu gặp mặt vậy. Nếu không có 'Session Management', thì mỗi lần mấy đứa chuyển trang, server lại hỏi: 'Bạn là ai? Đăng nhập lại đi!', hoặc 'Giỏ hàng của bạn đâu? Cho lại từ đầu đi!'. Nghe thôi đã thấy... phát bực rồi đúng không? Session Management ra đời để giải quyết cái sự 'mất trí nhớ' kinh niên của HTTP, biến một loạt các request rời rạc thành một 'phiên làm việc' liền mạch. Cách hoạt động 'bí mật' của Session Management Khi mấy đứa lần đầu ghé thăm website (hoặc đăng nhập), server sẽ tạo ra một cái 'session' mới, giống như mở một cái 'tủ locker' riêng cho mấy đứa vậy. Trong cái tủ đó, server sẽ lưu trữ những thông tin quan trọng về mấy đứa (ví dụ: đã đăng nhập chưa, ID user là gì, giỏ hàng có gì...). Sau đó, server sẽ gửi lại cho trình duyệt của mấy đứa một cái 'chìa khóa' (gọi là Session ID, thường được lưu trong một HTTP cookie). Mỗi lần mấy đứa gửi request tiếp theo, trình duyệt sẽ tự động gửi cái 'chìa khóa' này lên. Server chỉ việc dùng cái chìa khóa đó để mở đúng cái tủ locker của mấy đứa, lấy thông tin ra và biết 'À, đây là thằng X, nó muốn làm Y'. Nghe có vẻ phức tạp nhưng thực ra nó tự động hết, mấy đứa chỉ cần cấu hình thôi. Triển khai Session Management với Node.js và Express.js Trong Node.js, đặc biệt là với framework Express.js, việc quản lý session trở nên dễ như ăn kẹo nhờ thư viện express-session. Nó là 'trợ lý' đắc lực giúp chúng ta xây dựng cái hệ thống 'tủ locker' và 'chìa khóa' đó một cách hiệu quả. 1. Cài đặt: Đầu tiên, phải cài đặt 'trợ lý' đã chứ: npm install express express-session 2. Code Ví Dụ Minh Họa: Giờ thì cấu hình cho nó hoạt động trong ứng dụng Express của mấy đứa. Anh Creyt sẽ dùng một ví dụ đơn giản để mấy đứa dễ hình dung: const express = require('express'); const session = require('express-session'); const app = express(); const port = 3000; // Cấu hình middleware express-session app.use(session({ secret: 'anhcreytdayhocsession_sieubi_mat_1234567890', // Chuỗi bí mật dùng để ký session ID cookie resave: false, // Không lưu lại session nếu nó không được thay đổi saveUninitialized: false, // Không lưu session mới tạo nhưng chưa có dữ liệu cookie: { secure: false, // true nếu dùng HTTPS, false cho HTTP (dev) httpOnly: true, // Ngăn chặn truy cập cookie từ client-side JavaScript maxAge: 1000 * 60 * 60 * 24 // Thời gian sống của cookie session (ví dụ: 1 ngày) } })); // Middleware kiểm tra đăng nhập (ví dụ) function isAuthenticated(req, res, next) { if (req.session.userId) { // Kiểm tra xem session có chứa userId không next(); // Nếu có, cho phép đi tiếp } else { res.status(401).send('Bạn chưa đăng nhập. Vui lòng đăng nhập để truy cập.'); } } // Route trang chủ app.get('/', (req, res) => { if (req.session.userId) { res.send(`Chào mừng bạn đã trở lại, User ID: ${req.session.userId}! Lượt truy cập: ${req.session.views || 0}`); } else { res.send('Chào mừng bạn đến với trang chủ! Vui lòng đăng nhập.'); } }); // Route đăng nhập (giả lập) app.get('/login', (req, res) => { // Giả lập đăng nhập thành công req.session.userId = 'genz_coder_123'; // Lưu user ID vào session req.session.username = 'CreytJunior'; // Lưu thêm thông tin khác res.send('Đăng nhập thành công! Session của bạn đã được tạo.'); }); // Route trang cá nhân (cần đăng nhập) app.get('/profile', isAuthenticated, (req, res) => { res.send(`Đây là trang cá nhân của ${req.session.username} (ID: ${req.session.userId}).`); }); // Route đếm lượt truy cập (trong cùng một session) app.get('/views', (req, res) => { req.session.views = (req.session.views || 0) + 1; res.send(`Bạn đã ghé thăm trang này ${req.session.views} lần trong phiên này.`); }); // Route đăng xuất app.get('/logout', (req, res) => { req.session.destroy(err => { // Xóa session khỏi server if (err) { return res.status(500).send('Lỗi khi đăng xuất.'); } res.send('Bạn đã đăng xuất thành công.'); }); }); app.listen(port, () => { console.log(`Server đang chạy tại http://localhost:${port}`); }); Trong ví dụ trên, khi user truy cập /login, anh Creyt đã 'giả vờ' đăng nhập thành công và lưu userId cùng username vào req.session. Từ giờ, mỗi request tiếp theo từ trình duyệt đó sẽ mang theo cái cookie chứa Session ID, và server sẽ dùng nó để truy cập lại đúng cái req.session này, biết 'À, đây là thằng CreytJunior!'. Tuyệt vời chưa? Mẹo vặt để dùng Session Management 'chuẩn pro' (Best Practices) Dùng session thì sướng thật, nhưng phải dùng cho đúng cách, không thì 'toang' đấy mấy đứa. Đây là vài mẹo từ anh Creyt: secret key phải thật 'bí mật': Cái chuỗi secret này dùng để ký (sign) cái Session ID cookie. Nếu kẻ xấu biết được, chúng có thể giả mạo Session ID và chiếm quyền session của người khác (Session Hijacking). Hãy dùng một chuỗi ngẫu nhiên, dài, và phức tạp, đừng bao giờ để lộ ra ngoài! Mẹo: Dùng một thư viện như crypto để tạo chuỗi ngẫu nhiên, hoặc lưu nó trong biến môi trường (environment variable) và không hardcode như ví dụ trên (ví dụ trên chỉ để minh họa). Cấu hình cookie options 'chuẩn chỉ': Đây là lá chắn bảo vệ session của mấy đứa. httpOnly: true: Cực kỳ quan trọng! Ngăn chặn JavaScript ở client-side truy cập vào cookie Session ID. Hạn chế tấn công XSS (Cross-Site Scripting). secure: true: Chỉ gửi cookie qua kết nối HTTPS. Bắt buộc phải bật khi deploy lên production để tránh bị nghe lén (Man-in-the-Middle). Trong môi trường dev dùng HTTP thì để false. sameSite: 'lax' hoặc 'strict': Bảo vệ chống tấn công CSRF (Cross-Site Request Forgery) bằng cách kiểm soát việc cookie được gửi đi cùng với các request từ các trang web khác. 'lax' là lựa chọn cân bằng tốt. maxAge: Đặt thời gian hết hạn hợp lý cho session (ví dụ: 15 phút cho ngân hàng, 1 ngày cho mạng xã hội). Đừng để session tồn tại mãi mãi! Chọn 'nơi trú ngụ' cho session cẩn thận: Nơi lưu trữ session cũng quan trọng không kém. Mặc định, express-session dùng MemoryStore (lưu session trong bộ nhớ của server). Cái này chỉ để 'thử nghiệm' hoặc ứng dụng siêu nhỏ thôi. Nếu server restart là mất hết session. Production: Phải dùng các kho lưu trữ session chuyên dụng như Redis (cực nhanh, in-memory database, lý tưởng cho tốc độ) hoặc MongoDB/PostgreSQL (nếu cần bền vững hơn và có thể query session). Thư viện connect-redis hoặc connect-mongo sẽ giúp mấy đứa làm điều này. Khi nào thì resave: false và saveUninitialized: false?: Đây là cấu hình tiết kiệm tài nguyên. resave: false: Tiết kiệm tài nguyên. Chỉ lưu lại session vào kho nếu có sự thay đổi dữ liệu trong req.session. saveUninitialized: false: Tránh tạo ra quá nhiều session trống rỗng cho những user chỉ ghé thăm mà không tương tác gì. Thu hồi session khi đăng xuất: Luôn luôn gọi req.session.destroy() khi user đăng xuất để xóa session khỏi server và làm mất hiệu lực Session ID. Đừng bao giờ quên bước này! Ứng dụng thực tế của Session Management Mấy đứa cứ nhìn xung quanh mà xem, hầu hết các trang web lớn đều dùng Session Management đó: E-commerce (Shopee, Tiki, Amazon): Giỏ hàng của mấy đứa, trạng thái đăng nhập, lịch sử mua hàng... tất cả đều được duy trì qua session. Mạng xã hội (Facebook, Instagram): Mấy đứa đăng nhập một lần là có thể lướt feed, đăng bài, chat chit mà không cần đăng nhập lại liên tục. Ngân hàng trực tuyến: Các phiên giao dịch, thông tin tài khoản đều được bảo vệ và duy trì qua session. Nếu không có, mỗi lần chuyển trang lại phải nhập OTP thì... 'thôi rồi lượm ơi'! Các trang quản trị (Admin dashboards): Duy trì phiên làm việc của admin, quyền truy cập vào các module khác nhau. Khi nào thì 'triển' Session Management? Anh Creyt khuyên mấy đứa nên dùng Session Management khi: Cần duy trì trạng thái đăng nhập của người dùng: Đây là case phổ biến nhất. Cần lưu trữ dữ liệu tạm thời liên quan đến người dùng giữa các request: Ví dụ: giỏ hàng, tùy chọn cá nhân hóa, dữ liệu form nhiều bước. Cần một lớp bảo mật cao hơn cho dữ liệu người dùng so với việc lưu trực tiếp vào cookie: Session ID chỉ là một 'chìa khóa', dữ liệu thật sự nằm an toàn trên server. Ứng dụng của mấy đứa không phải là API thuần cho mobile app mà là một web app truyền thống (server-rendered hoặc SPA với backend session). Lời kết từ anh Creyt Tóm lại, Session Management là 'trái tim' của việc tương tác người dùng trên web, giúp server không 'mất trí nhớ' và mang lại trải nghiệm mượt mà, liền mạch. Nắm vững nó, mấy đứa sẽ tạo ra những ứng dụng web không chỉ mạnh mẽ mà còn thân thiện với người dùng. Nhớ nhé, 'secret' phải bí mật, cookie phải an toàn, và chọn đúng 'nơi trú ngụ' cho session. Cứ thế mà 'triển' thôi, Gen Z! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

83 Đọc tiếp
cookie-parser: Đọc 'Thư Tình' từ Trình Duyệt trong Node.js
23/03/2026

cookie-parser: Đọc 'Thư Tình' từ Trình Duyệt trong Node.js

Chào các bạn Gen Z mê code, tôi là Creyt đây! Hôm nay chúng ta sẽ cùng “mổ xẻ” một công cụ nhỏ nhưng có võ, đóng vai trò như một “thư ký chuyên nghiệp” trong thế giới Node.js của chúng ta: cookie-parser. 1. cookie-parser là gì và để làm gì? (Theo phong cách Gen Z) À mà này, các bạn có nhớ những mẩu giấy nhớ nhỏ xíu mà crush hay gửi gắm không? Kiểu như “Hôm nay ăn gì?” hay “Nhớ ôn bài nhé!” chẳng hạn. Mỗi lần nhận được, bạn phải tự tay bóc ra, đọc từng chữ, rồi sắp xếp vào một chỗ để dễ nhớ đúng không? Trong thế giới lập trình web, Cookie chính là những “mẩu giấy nhớ” mà trình duyệt (browser) để lại trên máy tính của người dùng và gửi ngược lại cho server mỗi khi họ ghé thăm một trang web nào đó. Những mẩu giấy này chứa thông tin quan trọng như: bạn là ai (ID session), bạn thích ngôn ngữ gì, bạn đã thêm gì vào giỏ hàng… Vấn đề là, khi những “mẩu giấy nhớ” này (cookie) được trình duyệt gửi lên server qua HTTP request, chúng thường ở dạng một chuỗi văn bản “rối rắm” trong phần header, kiểu như Cookie: name=Creyt; course=Nodejs; level=master. Đấy, nhìn là thấy “nhức cái đầu” rồi đúng không? Bạn phải tự ngồi cắt chuỗi, phân tích từng cặp key-value một cách thủ công. Mất thời gian, dễ lỗi, và không “chill” chút nào! cookie-parser chính là “thư ký” siêu năng lực của chúng ta! Nhiệm vụ của nó là tự động lấy cái chuỗi cookie “rối rắm” kia, “giải mã” và biến nó thành một object JavaScript siêu dễ dùng ({ name: 'Creyt', course: 'Nodejs', level: 'master' }). Object này sẽ được gắn vào đối tượng req của Express, cụ thể là req.cookies hoặc req.signedCookies. Nói cách khác, nó biến “rác” thành “vàng” (dữ liệu có cấu trúc) cho server của bạn, giúp bạn đọc và sử dụng cookie một cách “ngon lành cành đào” mà không cần phải động tay vào việc phân tích chuỗi lằng nhằng. 2. Code Ví Dụ Minh Họa Rõ Ràng Để bắt đầu, bạn cần cài đặt express và cookie-parser: npm install express cookie-parser Sau đó, đây là cách bạn sử dụng nó trong ứng dụng Express của mình: const express = require('express'); const cookieParser = require('cookie-parser'); // Import cookie-parser const app = express(); const port = 3000; // --- Cấu hình cookie-parser --- // Nếu không cần signed cookies, chỉ cần dùng app.use(cookieParser()); // Nếu dùng signed cookies, cần cung cấp một secret key. Key này phải đủ mạnh và giữ bí mật! const SECRET_KEY = 'day_la_mot_secret_key_sieu_bi_mat_cua_creyt_day_nha'; app.use(cookieParser(SECRET_KEY)); // Route để set (tạo) cookie app.get('/set-cookie', (req, res) => { // Set một cookie thông thường res.cookie('username', 'CreytGenz', { maxAge: 900000, httpOnly: true }); // Set một signed cookie (đã ký) // Signed cookie giúp kiểm tra xem cookie có bị thay đổi bởi client hay không. // Tuy nhiên, nó vẫn có thể bị đọc được. res.cookie('userId', '12345', { maxAge: 900000, httpOnly: true, signed: true // Đánh dấu đây là signed cookie }); res.send('Cookie đã được set! Mở DevTools -> Application -> Cookies để kiểm tra.'); }); // Route để đọc cookie app.get('/get-cookie', (req, res) => { // req.cookies chứa các cookie KHÔNG được ký (unsigned cookies) console.log('Unsigned Cookies:', req.cookies); // req.signedCookies chứa các cookie ĐÃ được ký (signed cookies) // Nếu signed cookie bị chỉnh sửa ở client, giá trị sẽ là false/undefined console.log('Signed Cookies:', req.signedCookies); let responseText = '<h2>Cookie bạn đã gửi lên:</h2>'; responseText += '<h3>Unsigned Cookies:</h3><pre>' + JSON.stringify(req.cookies, null, 2) + '</pre>'; responseText += '<h3>Signed Cookies:</h3><pre>' + JSON.stringify(req.signedCookies, null, 2) + '</pre>'; res.send(responseText); }); // Route kiểm tra cookie bị thay đổi app.get('/check-signed-cookie', (req, res) => { const userId = req.signedCookies.userId; if (userId) { res.send(`Chào mừng User ID: ${userId} (Cookie hợp lệ)!`); } else { res.send('Cookie userId không hợp lệ hoặc đã bị chỉnh sửa!'); } }); app.listen(port, () => { console.log(`Server đang chạy tại http://localhost:${port}`); console.log('Truy cập http://localhost:3000/set-cookie để tạo cookie.'); console.log('Sau đó truy cập http://localhost:3000/get-cookie để đọc cookie.'); console.log('Thử sửa cookie userId trong DevTools rồi truy cập /check-signed-cookie để xem điều gì xảy ra!'); }); Cách thử nghiệm: Chạy file Node.js trên. Mở trình duyệt, truy cập http://localhost:3000/set-cookie. Bạn sẽ thấy thông báo cookie đã được set. Mở DevTools (F12) -> tab Application -> mục Cookies. Bạn sẽ thấy username và userId. Truy cập http://localhost:3000/get-cookie. Bạn sẽ thấy server đã đọc được cả unsigned và signed cookies. Thử thách: Trong DevTools, click vào cookie userId, sửa giá trị của nó (ví dụ từ s%3A12345.xxxx thành s%3A99999.xxxx). Sau đó truy cập http://localhost:3000/check-signed-cookie. Bạn sẽ thấy server báo “Cookie userId không hợp lệ hoặc đã bị chỉnh sửa!” vì cookie-parser đã phát hiện ra chữ ký không khớp! 3. Mẹo (Best Practices) để ghi nhớ hoặc dùng thực tế Luôn dùng httpOnly: true: Đây là “kim bài” chống lại tấn công XSS (Cross-Site Scripting). Nó ngăn chặn JavaScript phía client đọc hoặc truy cập cookie của bạn. Giống như bạn cất nhật ký vào két sắt mà không ai ngoài bạn có chìa khóa vậy. Luôn dùng secure: true: Đảm bảo cookie chỉ được gửi qua kết nối HTTPS (kết nối bảo mật). Nếu trang web của bạn dùng HTTPS, hãy bật cái này lên. Nó giống như việc bạn chỉ gửi thư tình qua đường bưu điện bảo mật, không qua đường công cộng dễ bị đọc trộm. Dùng signed: true cho dữ liệu quan trọng: Như ví dụ trên, signed cookie giúp server phát hiện liệu cookie có bị chỉnh sửa bởi người dùng hay không. Nhưng nhớ, signed cookie chỉ kiểm tra tính toàn vẹn (integrity), không phải tính bảo mật (confidentiality). Kẻ gian vẫn có thể đọc được giá trị cookie đã ký. Đừng lưu mật khẩu hay thông tin cực kỳ nhạy cảm vào đây! SECRET_KEY phải là bí mật của riêng bạn! Không chia sẻ, không hardcode trong code production, mà nên lấy từ biến môi trường (environment variables). Key này càng phức tạp càng tốt, giống như mật khẩu ngân hàng vậy. Không lưu thông tin nhạy cảm trực tiếp vào Cookie: Thay vào đó, hãy lưu một ID session duy nhất vào cookie, rồi dùng ID đó để truy xuất thông tin nhạy cảm từ một session store an toàn trên server (ví dụ: Redis, MongoDB). Cookie dễ bị tấn công CSRF, XSS nếu không cẩn thận. Cẩn thận với maxAge và expires: Đây là thời gian sống của cookie. Đặt quá dài có thể gây rủi ro bảo mật, đặt quá ngắn thì người dùng lại phải đăng nhập lại liên tục. Cân nhắc kỹ cho từng trường hợp. 4. Ứng dụng thực tế các website/ứng dụng đã dùng Hầu hết mọi ứng dụng web hiện đại đều dùng cookie và dĩ nhiên là phải có một cơ chế để đọc chúng: Ghi nhớ đăng nhập (Remember Me): Khi bạn tick “Ghi nhớ đăng nhập” trên Facebook, Google, hay bất kỳ trang nào, server sẽ gửi một cookie chứa session ID hoặc refresh token xuống trình duyệt. Lần sau, khi bạn quay lại, trình duyệt gửi cookie này lên, và cookie-parser sẽ giúp server đọc nó để biết bạn là ai mà không cần đăng nhập lại. Giỏ hàng điện tử: Các trang thương mại điện tử như Tiki, Shopee thường dùng cookie để lưu trữ ID giỏ hàng của bạn. Khi bạn thêm sản phẩm vào giỏ, server sẽ lưu thông tin giỏ hàng vào database và gửi một cookie chứa ID giỏ hàng đó xuống. Lần sau, bạn quay lại, server đọc ID cookie để hiển thị giỏ hàng của bạn. Cá nhân hóa trải nghiệm: Lưu trữ tùy chọn ngôn ngữ, theme (sáng/tối), hoặc các thiết lập giao diện người dùng khác của bạn để mỗi khi bạn truy cập lại, trang web đã sẵn sàng với những gì bạn thích. Theo dõi hành vi người dùng: Các nền tảng quảng cáo hoặc phân tích (như Google Analytics) dùng cookie để theo dõi lượt truy cập, trang bạn đã xem, thời gian bạn ở lại… (nhưng nhớ là phải tuân thủ các quy định về quyền riêng tư như GDPR/CCPA nhé). 5. Thử nghiệm đã từng và hướng dẫn nên dùng cho case nào Với kinh nghiệm của Creyt, cookie-parser là một trong những middleware thiết yếu mà bạn sẽ dùng trong hầu hết mọi ứng dụng Node.js/Express có tương tác với cookie. Nó giải quyết một vấn đề cơ bản: làm sao để đọc dữ liệu từ cookie một cách dễ dàng và an toàn? Nên dùng cho các trường hợp: Quản lý Session: Khi bạn xây dựng hệ thống đăng nhập/đăng ký, bạn sẽ cần cookie để lưu trữ Session ID. cookie-parser là bước đầu tiên để server có thể đọc được Session ID đó và xác định người dùng. Lưu trữ tùy chọn người dùng: Ngôn ngữ, theme, cài đặt hiển thị… những thứ mà người dùng muốn được giữ lại giữa các lần truy cập. Tích hợp với các dịch vụ bên thứ ba: Một số API hoặc dịch vụ có thể gửi cookie xuống trình duyệt và bạn cần server đọc lại chúng để xử lý. Không nên dùng khi: Bạn cần lưu trữ lượng lớn dữ liệu (cookie có giới hạn kích thước, thường là 4KB mỗi domain). Lúc này, nên dùng Session Store hoặc Local Storage/IndexedDB ở phía client. Bạn cần bảo mật tuyệt đối cho dữ liệu. Cookie, dù có signed hay httpOnly, vẫn không phải là nơi an toàn nhất để lưu trữ thông tin cực kỳ nhạy cảm. Luôn dùng session store trên server cho những dữ liệu đó. Hy vọng với bài giảng này, các bạn đã hiểu rõ hơn về cookie-parser và biết cách biến những “mẩu giấy nhớ” của trình duyệt thành dữ liệu có ích cho ứng dụng của mình. Nhớ nhé, code hay là phải thực tế và an toàn! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

66 Đọc tiếp
Giải Mã Body-Parser: Shipper Dữ Liệu Của Server Node.js
23/03/2026

Giải Mã Body-Parser: Shipper Dữ Liệu Của Server Node.js

Chào các "coder nhí" của thầy Creyt! Hôm nay, chúng ta sẽ cùng "flex" kiến thức về một "công cụ" mà dân làm backend Node.js nào cũng phải biết, đó là body-parser. Nghe tên có vẻ "chill phết" nhưng công dụng của nó thì "căng đét" luôn đấy! body-parser là gì và để làm gì? (Giải thích kiểu Gen Z) Thế này nhé, các bạn cứ hình dung server Node.js của chúng ta giống như một "anh shipper" siêu cấp "đỉnh của chóp" đang chờ nhận hàng. Khi client (trình duyệt, ứng dụng mobile, Postman...) gửi dữ liệu lên server thông qua các phương thức như POST, PUT, PATCH, thì cái dữ liệu đó nó không tự động "biến hình" thành một object JavaScript mà server có thể đọc được ngay đâu. Nó giống như một gói hàng được đóng gói "kín mít" mà anh shipper phải tự tay bóc ra, xem bên trong là gì, rồi mới biết cách xử lý. body-parser chính là "nhân viên kiểm tra và phân loại hàng hóa" chuyên nghiệp ở trung tâm vận chuyển của anh shipper. Nhiệm vụ của nó là: bóc tách, đọc nhãn (định dạng dữ liệu như JSON, URL-encoded) và chuyển đổi cái gói hàng "lằng nhằng" đó thành một object JavaScript "sạch sẽ", dễ hiểu" để anh server của chúng ta có thể làm việc ngay mà không cần "đau đầu" suy nghĩ. Nói cách khác, body-parser giúp server Node.js (cụ thể hơn là Express.js) của bạn "hiểu" được dữ liệu mà client gửi lên trong phần body của HTTP request. Không có nó, bạn sẽ chỉ nhận được một "dòng sông" dữ liệu thô (raw stream of bytes) mà thôi, và việc xử lý nó sẽ "khó nhằn" như chơi game mà không có cheat code vậy! Code Ví Dụ Minh Hoạ Rõ Ràng, Chuẩn Kiến Thức Để hiểu rõ hơn, chúng ta cùng "coding" một chút nhé. Đầu tiên, bạn cần khởi tạo project Node.js và cài đặt Express (và body-parser nếu dùng bản cũ) như sau: npm init -y npm install express body-parser 1. Server "mù chữ" không có body-parser (để thấy vấn đề): Nếu không dùng body-parser, khi bạn gửi dữ liệu POST, req.body sẽ là undefined. // app_without_parser.js const express = require('express'); const app = express(); const PORT = 3000; app.use(express.json()); // Dù có cái này nhưng nó chỉ parse JSON. Nếu gửi form URL-encoded vẫn tạch. app.post('/data', (req, res) => { console.log('Dữ liệu nhận được (req.body):', req.body); res.send(`Bạn đã gửi: ${JSON.stringify(req.body)}`); }); app.listen(PORT, () => { console.log(`Server chạy trên cổng ${PORT}.`); console.log('Thử gửi POST request đến http://localhost:3000/data với dữ liệu JSON hoặc form-urlencoded.'); }); Nếu bạn gửi một request POST với Content-Type: application/x-www-form-urlencoded tới /data, req.body sẽ là undefined (trừ khi bạn dùng express.urlencoded()). 2. Sử dụng body-parser để "khai sáng" server: body-parser cung cấp các middleware khác nhau để xử lý các loại Content-Type khác nhau: bodyParser.json(): Dùng cho dữ liệu JSON (Content-Type: application/json). bodyParser.urlencoded(): Dùng cho dữ liệu form URL-encoded (Content-Type: application/x-www-form-urlencoded). bodyParser.raw(): Dùng cho dữ liệu nhị phân. bodyParser.text(): Dùng cho dữ liệu văn bản thuần túy. Chúng ta sẽ tập trung vào json và urlencoded vì chúng phổ biến nhất. // app_with_parser.js const express = require('express'); const bodyParser = require('body-parser'); // Import body-parser const app = express(); const PORT = 3000; // 1. Sử dụng body-parser cho JSON data // app.use(bodyParser.json()); // Cách dùng cũ app.use(express.json()); // Cách dùng mới, được tích hợp sẵn trong Express 4.16.0+ // 2. Sử dụng body-parser cho URL-encoded data // app.use(bodyParser.urlencoded({ extended: true })); // Cách dùng cũ app.use(express.urlencoded({ extended: true })); // Cách dùng mới, được tích hợp sẵn trong Express 4.16.0+ // extended: true cho phép parse các object và array lồng nhau // extended: false chỉ parse string hoặc array đơn giản app.get('/', (req, res) => { res.send('Chào mừng đến với server của thầy Creyt!'); }); // Route xử lý dữ liệu JSON app.post('/api/users', (req, res) => { const newUser = req.body; console.log('Dữ liệu người dùng nhận được (JSON):', newUser); if (newUser && newUser.name && newUser.email) { res.status(201).json({ message: 'Người dùng đã được tạo thành công!', user: newUser }); } else { res.status(400).json({ message: 'Dữ liệu không hợp lệ. Cần có tên và email.' }); } }); // Route xử lý dữ liệu form URL-encoded app.post('/submit-form', (req, res) => { const formData = req.body; console.log('Dữ liệu form nhận được (URL-encoded):', formData); if (formData && formData.username && formData.password) { res.status(200).send(`Đăng nhập thành công cho user: ${formData.username}`); } else { res.status(400).send('Dữ liệu form không hợp lệ. Cần có username và password.'); } }); app.listen(PORT, () => { console.log(`Server chạy trên cổng ${PORT}.`); console.log('Thử gửi POST request đến http://localhost:3000/api/users (JSON) hoặc http://localhost:3000/submit-form (URL-encoded).'); }); Cách kiểm tra với Postman/Insomnia: Để test /api/users: Chọn phương thức POST. URL: http://localhost:3000/api/users. Tab Body, chọn raw, sau đó chọn JSON (application/json). Nhập JSON: {"name": "Creyt", "email": "creyt@example.com", "age": 30} Để test /submit-form: Chọn phương thức POST. URL: http://localhost:3000/submit-form. Tab Body, chọn x-www-form-urlencoded. Nhập các cặp key-value: username: creyt_dev, password: supersecret Mẹo (Best Practices) để ghi nhớ hoặc dùng thực tế "Update" kiến thức: Từ phiên bản Express 4.16.0 trở lên, các chức năng của body-parser đã được tích hợp thẳng vào Express rồi các bạn ạ! Tức là, thay vì require('body-parser') rồi dùng bodyParser.json() hay bodyParser.urlencoded(), bạn có thể dùng thẳng express.json() và express.urlencoded(). Điều này "ngầu" hơn, gọn gàng hơn và là cách hiện đại để làm việc. Mẹo: Coi express.json() và express.urlencoded() là phiên bản "upgrade" của body-parser. extended: true hay false? Hầu hết các trường hợp, bạn sẽ dùng extended: true vì nó cho phép parse dữ liệu phức tạp hơn như nested objects (object lồng object) hay arrays. Nếu bạn chỉ cần dữ liệu đơn giản (key-value strings), false cũng được, nhưng true là "tiêu chuẩn" hiện tại. Đặt đúng chỗ: Luôn app.use() các middleware parse body trước các route handler mà bạn muốn xử lý dữ liệu. Nếu không, các route handler đó sẽ không thấy req.body đã được parse đâu. Giới hạn kích thước payload: Để tránh các cuộc tấn công DoS (Denial of Service) bằng cách gửi dữ liệu quá lớn, bạn nên giới hạn kích thước payload. Cả express.json() và express.urlencoded() đều có tùy chọn limit. app.use(express.json({ limit: '10kb' })); // Giới hạn JSON payload tối đa 10KB app.use(express.urlencoded({ extended: true, limit: '10kb' })); // Giới hạn URL-encoded payload tối đa 10KB Chỉ dùng khi cần: Nếu một route nào đó của bạn không bao giờ nhận dữ liệu trong body (ví dụ: các route GET), thì không cần áp dụng middleware body-parser cho route đó. Tuy nhiên, việc áp dụng toàn cục app.use() là phổ biến và thường không gây vấn đề hiệu suất đáng kể. Ví dụ thực tế các ứng dụng/website đã ứng dụng Hầu hết mọi ứng dụng web hoặc API backend sử dụng Node.js và Express đều dùng đến cơ chế tương tự body-parser (dù là body-parser gốc hay express.json()/express.urlencoded()). Các API RESTful: Bất kỳ API nào cho phép bạn tạo (POST), cập nhật (PUT/PATCH) tài nguyên (ví dụ: tạo tài khoản người dùng, đăng sản phẩm mới, cập nhật thông tin cá nhân) đều phải đọc dữ liệu từ request body, thường là JSON. Ví dụ: API của Facebook, Instagram, Shopee, Tiki khi bạn đăng nhập, đăng bài, mua hàng... Form đăng ký/đăng nhập: Khi bạn điền form đăng ký hoặc đăng nhập trên một website, dữ liệu thường được gửi dưới dạng application/x-www-form-urlencoded. Server sẽ dùng body-parser (hoặc express.urlencoded()) để đọc username, password và các thông tin khác. Upload file (với form data): Mặc dù body-parser không trực tiếp xử lý upload file lớn (thường cần các thư viện như multer), nhưng nó vẫn là nền tảng để xử lý các trường văn bản khác trong form multipart/form-data. Thử nghiệm đã từng và hướng dẫn nên dùng cho case nào Thầy Creyt đã từng "combat" với Node.js từ những ngày đầu, khi mà việc parse body request còn "thủ công" bằng cách lắng nghe event data và end trên req stream. Nó "khó nhằn" và "dễ lỗi" lắm! body-parser ra đời như một vị cứu tinh, giúp developer "nhẹ gánh" hơn rất nhiều. Khi nào nên dùng (hoặc dùng express.json/express.urlencoded): Xây dựng API RESTful: Đây là trường hợp phổ biến nhất. Hầu hết các API đều nhận dữ liệu JSON để tạo hoặc cập nhật tài nguyên. Xử lý form HTML: Khi bạn có các form POST thông thường trên website, dữ liệu sẽ được gửi dưới dạng URL-encoded. Nhận dữ liệu từ webhook: Nhiều dịch vụ (như Stripe, GitHub, Slack) gửi dữ liệu qua webhook dưới dạng JSON khi có sự kiện xảy ra. Server của bạn cần parse JSON đó. Tóm lại: Bất cứ khi nào server của bạn cần đọc dữ liệu được gửi trong phần body của một HTTP request (thường là POST, PUT, PATCH), bạn sẽ cần đến "nhân viên phân loại hàng hóa" body-parser (hoặc phiên bản "nâng cấp" express.json()/express.urlencoded()). Nó là một phần không thể thiếu để server của bạn "thông minh" và "khét lẹt" hơn trong việc xử lý dữ liệu client gửi lên đấy! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

63 Đọc tiếp
Morgan Logger: Thám Tử Đắc Lực Của Server Node.js
23/03/2026

Morgan Logger: Thám Tử Đắc Lực Của Server Node.js

Morgan Logger: Thám Tử Đắc Lực Của Server Node.js Chào các chiến thần Gen Z của anh Creyt! Hôm nay, chúng ta sẽ cùng nhau khám phá một "thám tử" cực kỳ đắc lực trong thế giới Node.js/Express, đó chính là Morgan Logger. Nghe tên có vẻ "ngầu lòi" đúng không? Đảm bảo sau buổi này, các em sẽ "flex" được ngay là mình đã biến server thành một cuốn nhật ký siêu chi tiết, không sót một "drama" nào! Morgan Logger là gì mà "chill" thế? Thử tưởng tượng thế này nhé: Server Node.js của các em như một quán cà phê đông đúc. Khách ra vào tấp nập, gọi món này món kia. Nếu không có ai ghi chép lại, làm sao các em biết được ai vào, gọi gì, bao lâu thì đi, có món nào bị phàn nàn không? Chắc chắn là loạn xì ngầu! Morgan Logger chính là cái "anh quản lý sổ sách" siêu tỉ mỉ đó. Nó là một middleware (nhớ khái niệm middleware anh đã giảng chưa? Như một người gác cổng kiểm tra mọi thứ trước khi cho vào nhà vậy) dành cho Express.js, chuyên trách nhiệm vụ ghi lại (log) mọi request HTTP gửi đến server của các em. Nói cách khác, mỗi khi có một "khách hàng" (request) gõ cửa server, Morgan sẽ chụp lại một tấm ảnh "thẻ căn cước" của request đó: nó đến từ đâu (IP), vào lúc nào, dùng phương thức gì (GET, POST, PUT, DELETE), đường dẫn là gì, trạng thái trả về ra sao (thành công 200 OK hay lỗi 404 Not Found), và thậm chí cả thời gian xử lý request đó mất bao lâu. Tất cả đều được ghi lại cẩn thận vào "cuốn sổ nhật ký" của server, hay chính là console (hoặc file log) của các em. Để làm gì á? Quá nhiều thứ luôn! Debug thần sầu: Khi code bị lỗi, thay vì mò kim đáy bể, các em có thể nhìn vào log của Morgan để biết chính xác request nào đã gây ra lỗi, data gửi lên có đúng không, server phản hồi thế nào. Nó như một cái camera giám sát giúp các em tua lại "hiện trường" vậy. Theo dõi hiệu năng: Biết được mỗi request mất bao lâu để xử lý giúp các em tối ưu hóa code, tìm ra những chỗ "nghẽn cổ chai" làm chậm server. Phân tích hành vi người dùng (cơ bản): Dù không chi tiết bằng các công cụ analytics chuyên dụng, nhưng việc biết request nào được gọi nhiều nhất, từ những IP nào, cũng cho các em cái nhìn tổng quan về cách người dùng tương tác với ứng dụng. Bảo mật: Phát hiện các request đáng ngờ, các cuộc tấn công DDoS cơ bản bằng cách theo dõi tần suất và loại request. Code Ví Dụ Minh Hoạ: Bắt tay vào "thực chiến"! Đầu tiên, các em cần cài đặt Morgan và Express (nếu chưa có): npm install express morgan Sau đó, hãy cùng xem một ví dụ đơn giản để thấy Morgan hoạt động như thế nào: const express = require('express'); const morgan = require('morgan'); const app = express(); const port = 3000; // Bước 1: Kích hoạt Morgan Logger // 'dev' là một trong những định dạng log có sẵn của Morgan, rất tiện cho môi trường phát triển app.use(morgan('dev')); // Định nghĩa một vài route đơn giản app.get('/', (req, res) => { res.send('Chào mừng đến với server của Creyt! Trang chủ đây!'); }); app.get('/users', (req, res) => { console.log('Đang xử lý request /users...'); res.json([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]); }); app.post('/products', (req, res) => { console.log('Có request POST đến /products'); res.status(201).send('Sản phẩm đã được tạo thành công!'); }); // Server bắt đầu lắng nghe app.listen(port, () => { console.log(`Server của Creyt đang chạy ở http://localhost:${port}`); }); Cách chạy: Lưu code trên vào file app.js. Mở terminal, chạy node app.js. Mở trình duyệt hoặc Postman, truy cập http://localhost:3000/ rồi http://localhost:3000/users. Thử gửi một request POST đến http://localhost:3000/products bằng Postman hoặc curl. Các em sẽ thấy những dòng log xuất hiện trên terminal tương tự như thế này (khi dùng format dev): GET / 304 - 2.872 ms GET /users 200 42 - 1.096 ms POST /products 201 29 - 0.789 ms Hiểu chứ? Mỗi dòng là một "câu chuyện" của một request: phương thức (GET/POST), đường dẫn, trạng thái HTTP (200, 304, 201), dung lượng response, và thời gian xử lý. Mẹo (Best Practices) để ghi nhớ và dùng thực tế Với một "thám tử" như Morgan, chúng ta cần biết cách dùng nó hiệu quả nhất để không bị "ngập lụt" trong thông tin mà vẫn tìm ra được "manh mối" cần thiết: Chọn "trang phục" phù hợp (Format): Morgan có nhiều "bộ cánh" (format) khác nhau: 'dev': Dành cho môi trường phát triển (development). Nó nhiều màu sắc, rất dễ đọc và cung cấp đủ thông tin cần thiết. Như ở ví dụ trên. 'tiny', 'short', 'common', 'combined': Cung cấp các mức độ chi tiết khác nhau. combined thường được dùng cho production vì nó ghi lại nhiều thông tin hơn, hữu ích cho phân tích sau này. Mẹo: Để nhớ, dev là để "dev", combined là để "production" (vì nó "kết hợp" nhiều thông tin hơn). "Cuốn nhật ký" không giới hạn (Lưu log vào file): Console thì tiện thật, nhưng nếu server chạy lâu hoặc gặp lỗi, log sẽ bị trôi đi mất. Hãy hướng Morgan ghi log vào một file để lưu trữ lâu dài. Đây là "real deal" khi deploy ứng dụng: const fs = require('fs'); const path = require('path'); // Tạo một stream để ghi log vào file access.log const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }); // Sử dụng Morgan với format 'combined' và stream là file access.log app.use(morgan('combined', { stream: accessLogStream })); // Log ra console luôn (tùy chọn) app.use(morgan('dev')); Với cách này, các em vừa có log trên console khi dev, vừa có log lưu vào file khi chạy production. "Hai mũi tên trúng hai đích"! "Đo ni đóng giày" (Custom Format): Các em muốn log thêm thông tin riêng của ứng dụng, ví dụ như ID của user đang login? Morgan cho phép tạo custom token và custom format. Điều này cực kỳ mạnh mẽ! // Tạo một token tùy chỉnh để log User ID (ví dụ: lấy từ req.user.id sau khi authenticate) morgan.token('user-id', function (req, res) { // Giả sử req.user tồn tại sau khi authenticate return req.user ? req.user.id : 'anonymous'; }); // Sử dụng custom format app.use(morgan(':method :url :status :response-time ms - user-id::user-id')); Với cách này, log của các em sẽ bao gồm cả user ID, giúp việc debug và theo dõi trở nên siêu chi tiết. "Đội hình siêu cấp" (Kết hợp với các Logger khác): Morgan rất tốt cho HTTP requests, nhưng nếu muốn log các sự kiện khác trong ứng dụng (ví dụ: lỗi database, thông báo quan trọng), các em nên kết hợp với các thư viện logger mạnh mẽ hơn như Winston hoặc Pino. Chúng sẽ giúp các em quản lý log một cách chuyên nghiệp hơn, có thể gửi log đến các dịch vụ lưu trữ tập trung (như ELK stack, Splunk). "Bảo mật thông tin" (Cẩn thận với dữ liệu nhạy cảm): Đừng bao giờ log trực tiếp các thông tin nhạy cảm như mật khẩu, token API, thông tin thẻ tín dụng vào log. Kẻ xấu có thể lợi dụng để đánh cắp thông tin. Hãy luôn lọc bỏ hoặc che giấu các trường dữ liệu này trước khi ghi vào log. Ứng dụng thực tế: "Ai đã dùng Morgan?" Hầu hết mọi ứng dụng web sử dụng Node.js và Express đều ít nhiều sử dụng Morgan (hoặc một logger tương tự). Từ các startup nhỏ đến các công ty lớn, Morgan là một công cụ không thể thiếu để duy trì sự ổn định và hiệu quả của server. Các trang thương mại điện tử: Theo dõi các yêu cầu thêm sản phẩm vào giỏ hàng, thanh toán, xử lý đơn hàng. Các API backend: Giám sát các cuộc gọi API từ ứng dụng di động hoặc frontend, đảm bảo các endpoint hoạt động đúng đắn. Các nền tảng mạng xã hội: Theo dõi các hoạt động đăng bài, bình luận, tương tác của người dùng để phát hiện các vấn đề tiềm ẩn. Nói chung, bất cứ nơi nào có server Express, ở đó có thể có Morgan đang âm thầm làm nhiệm vụ "ghi chép" của mình. Thử nghiệm và hướng dẫn nên dùng cho case nào Anh Creyt từng có lần debug một lỗi "lạ" mà chỉ xảy ra trên môi trường production. Không có Morgan, anh đã phải mất cả ngày trời mò mẫm. Nhưng khi bật Morgan với combined format và lưu vào file, anh chỉ mất vài phút để tìm ra rằng đó là do một request gửi thiếu header Authorization. Morgan đã ghi rõ ràng 401 Unauthorized và thiếu thông tin header trong log. Khi nào nên dùng Morgan? Debug "khẩn cấp": Khi có lỗi khó hiểu và cần nhìn rõ luồng request/response. Giám sát hiệu suất "real-time": Xem request nào mất nhiều thời gian nhất để xử lý. Phân tích hành vi "sơ bộ": Hiểu được tổng quan các request đến server. Kiểm tra bảo mật: Phát hiện các request đáng ngờ, ví dụ như quá nhiều request từ cùng một IP trong thời gian ngắn (có thể là tấn công DDoS hoặc brute-force). Thử nghiệm ngay và luôn: Chạy server với các format khác nhau: Tự mình thay đổi app.use(morgan('dev')) thành app.use(morgan('tiny')), app.use(morgan('combined')) và quan sát sự khác biệt trong console. Các em sẽ hiểu rõ hơn về từng loại format. Gửi đủ loại request: Dùng Postman hoặc curl để gửi GET, POST, PUT, DELETE đến các endpoint khác nhau, cả những endpoint không tồn tại (để tạo lỗi 404). Quan sát Morgan ghi lại chúng như thế nào. Tạo custom token của riêng mình: Hãy thử tạo một token để log thêm một thông tin nào đó mà các em nghĩ là quan trọng cho ứng dụng của mình (ví dụ: tên ứng dụng, phiên bản API). Nhớ nhé, Morgan không chỉ là một công cụ, nó là đôi mắt và đôi tai của các em trong thế giới server. Nắm vững nó, các em sẽ kiểm soát được "câu chuyện" của ứng dụng mình một cách chủ động và hiệu quả hơn rất nhiều. Cứ "chill" mà học, có gì khó cứ hỏi anh Creyt! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

44 Đọc tiếp
Helmet.js: Áo Giáp Chống Hacker Cho App Node.js Của Gen Z!
23/03/2026

Helmet.js: Áo Giáp Chống Hacker Cho App Node.js Của Gen Z!

Chào các 'developer tương lai' của Creyt! Hôm nay, chúng ta sẽ cùng nhau 'mổ xẻ' một 'công cụ' cực kỳ quan trọng, mà nếu không có nó, app của các bạn sẽ 'trần truồng' trước mọi hiểm nguy trên mạng. Nghe có vẻ 'drama' nhỉ? Nhưng tin Creyt đi, nó quan trọng thật đấy! Creyt đang nói về Helmet package trong Node.js. Helmet là gì mà nghe 'ngầu' vậy anh Creyt? Tưởng tượng thế này: các bạn vừa code xong một con app Node.js 'xịn xò', đẹp lung linh, logic mượt mà. Nhưng mà, app của các bạn cũng giống như một tòa nhà mới xây, cửa kính trong suốt, nội thất sang chảnh. Nếu không có 'hàng rào', 'camera an ninh', hay 'bảo vệ', thì sớm muộn gì cũng có 'kẻ gian' dòm ngó, đột nhập thôi, đúng không? Helmet.js chính là 'áo giáp', là 'bộ bảo vệ an ninh' cho ứng dụng Node.js của các bạn. Nó không phải là một viên đạn bạc chống lại mọi loại tấn công (ví dụ, nó không bảo vệ khỏi SQL Injection hay XSS trong code logic của bạn), nhưng nó là một 'tấm khiên' cực kỳ hiệu quả để chống lại những cuộc tấn công web phổ biến, mà thường được thực hiện bằng cách lợi dụng các lỗ hổng trong các HTTP headers. Nói một cách 'học thuật' hơn, Helmet là một tập hợp các middleware trong Express/Connect, giúp thiết lập các HTTP headers liên quan đến bảo mật. Mỗi middleware nhỏ trong Helmet sẽ lo một 'mảng' bảo mật riêng, giống như các 'vệ sĩ' chuyên biệt vậy. Vậy nó để làm gì? Đơn giản là để app của bạn 'sống sót' trên môi trường internet 'khắc nghiệt' này. Nó giúp ngăn chặn: Clickjacking: Kẻ xấu lừa người dùng click vào một thứ khác trên trang web của chúng. XSS (Cross-Site Scripting): Dù không hoàn toàn, nhưng nó có thể làm giảm rủi ro bằng cách giới hạn nơi script có thể chạy. MIME-type sniffing: Trình duyệt đoán sai kiểu nội dung, dẫn đến thực thi mã độc. Các cuộc tấn công SSL/TLS cũ: Đảm bảo trình duyệt chỉ kết nối qua HTTPS. Và nhiều 'trò mèo' khác của hacker. Code Ví Dụ Minh Họa: Mặc 'Áo Giáp' Cho App Ngay! Để thấy sức mạnh của Helmet, chúng ta cùng 'xắn tay áo' code một chút nhé. Creyt sẽ dùng Express.js vì nó là 'bạn thân' của Node.js trong việc xây dựng ứng dụng web. Đầu tiên, các bạn cần cài đặt Helmet: npm install express helmet Sau đó, áp dụng nó vào app của bạn: const express = require('express'); const helmet = require('helmet'); // Gọi anh vệ sĩ Helmet vào const app = express(); // Áp dụng Helmet.js như một middleware toàn cục // Giống như việc bạn mặc áo giáp trước khi ra trận vậy! app.use(helmet()); // Bây giờ, hãy xem xét một số cấu hình cụ thể hơn của Helmet // (Thường thì app.use(helmet()) đã kích hoạt hầu hết các tính năng mặc định) // Ví dụ: Muốn tùy chỉnh Content Security Policy (CSP) // CSP giống như 'danh sách trắng' cho phép những tài nguyên nào được tải trên trang của bạn. // Nó cực kỳ mạnh mẽ để chống lại XSS. app.use(helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], // Chỉ cho phép tải tài nguyên từ cùng một nguồn scriptSrc: ["'self'", "https://unpkg.com"], // Cho phép script từ nguồn của bạn và unpkg styleSrc: ["'self'", "'unsafe-inline'"], // Cho phép style từ nguồn của bạn và inline style (cẩn thận với cái này!) imgSrc: ["'self'", "data:", "https://images.unsplash.com"], // Cho phép ảnh từ nguồn của bạn, base64 và Unsplash connectSrc: ["'self'", "https://api.example.com"], // Cho phép kết nối API đến example.com }, })); // Hoặc muốn cấu hình Strict-Transport-Security (HSTS) // Cái này buộc trình duyệt chỉ được kết nối qua HTTPS cho domain của bạn trong một khoảng thời gian nhất định. // Giống như bạn ra lệnh: "Mày chỉ được đi đường cao tốc an toàn thôi!" app.use(helmet.hsts({ maxAge: 31536000, // 1 năm includeSubDomains: true, // Áp dụng cho cả các subdomain preload: true // Đăng ký với trình duyệt để tải trước (khuyến nghị cho production) })); // Ngăn chặn trình duyệt 'đoán mò' MIME type (X-Content-Type-Options: nosniff) // Cái này quan trọng để ngăn chặn các cuộc tấn công kiểu file được thực thi như script. app.use(helmet.noSniff()); // Ngăn chặn Clickjacking (X-Frame-Options: DENY) // Đảm bảo trang của bạn không bị nhúng vào iframe của trang khác. app.use(helmet.frameguard({ action: 'deny' })); // Route đơn giản để kiểm tra app.get('/', (req, res) => { res.send('Chào mừng đến với app được bảo vệ bởi Helmet của anh Creyt!'); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server đang chạy trên cổng ${PORT}. Hãy kiểm tra các HTTP headers!`); }); Sau khi chạy đoạn code này và truy cập http://localhost:3000, hãy mở Developer Tools (F12), vào tab Network, refresh trang và xem các HTTP Response Headers. Các bạn sẽ thấy một loạt các headers mới toanh do Helmet thêm vào, ví dụ: Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, v.v. Đó chính là 'bộ giáp' của bạn đấy! Mẹo 'Pro' Từ Anh Creyt (Best Practices) Luôn dùng trong Production: Đây là điều KHÔNG THỂ THIẾU cho bất kỳ ứng dụng web nào. Đừng bao giờ nghĩ "app nhỏ, không cần đâu". Hacker không phân biệt app to app nhỏ đâu em ơi! Hiểu rõ từng 'vệ sĩ': Mặc dù app.use(helmet()) rất tiện lợi vì nó kích hoạt hầu hết các middleware mặc định, nhưng hãy dành thời gian đọc tài liệu để hiểu từng header làm gì. Đặc biệt là Content-Security-Policy (CSP). CSP có thể là con dao hai lưỡi nếu cấu hình sai, nó có thể chặn cả những tài nguyên hợp lệ của bạn. Customize có chọn lọc: Không phải lúc nào cũng cần bật tất cả các tính năng của Helmet, hoặc bạn cần tùy chỉnh chúng. Ví dụ, frameguard có thể cần SAMEORIGIN thay vì DENY nếu bạn muốn nhúng trang của mình vào một iframe trên chính domain của mình. Kết hợp với các 'chiến thuật' khác: Helmet là lớp bảo vệ ở tầng HTTP headers. Nó không thay thế việc validate input, sanitize output, hay dùng ORM để chống SQL Injection. Hãy coi nó là một phần trong chiến lược bảo mật tổng thể của bạn. Kiểm tra thường xuyên: Sau khi cấu hình Helmet, hãy dùng các công cụ như curl -I http://localhost:3000 hoặc các công cụ kiểm tra bảo mật online để đảm bảo các headers đã được thiết lập đúng. Ứng dụng Thực Tế: Ai Dùng Helmet? Thực ra, câu hỏi nên là: "Ứng dụng Node.js nào KHÔNG dùng Helmet?" Các bạn có thể yên tâm rằng hầu hết các ứng dụng web lớn nhỏ, từ các startup 'non trẻ' đến các 'ông lớn' như Netflix, Uber (nếu họ dùng Node.js ở backend), đều sẽ áp dụng các biện pháp bảo mật HTTP header tương tự như Helmet cung cấp. Tưởng tượng một ngân hàng online, hay một sàn thương mại điện tử lớn, nếu không có những lớp bảo vệ này, thì việc thông tin khách hàng bị lộ, hay website bị tấn công clickjacking là điều hoàn toàn có thể xảy ra. Helmet giúp họ 'ngủ ngon' hơn một chút, biết rằng ít nhất tầng header của họ đã được 'đóng gói' cẩn thận. Thử Nghiệm và Khi Nào Nên Dùng? Thử nghiệm: Bước 1: Chạy app của bạn mà KHÔNG có app.use(helmet()). Dùng curl -I http://localhost:3000 và ghi lại các headers. Bước 2: Thêm app.use(helmet()). Chạy lại và so sánh các headers. Các bạn sẽ thấy sự khác biệt rõ rệt. Bước 3 (Nâng cao): Tắt từng middleware con của Helmet (ví dụ: app.use(helmet({ frameguard: false }))) để hiểu rõ từng cái ảnh hưởng thế nào đến headers. Khi nào nên dùng? Câu trả lời ngắn gọn là: LUÔN LUÔN! Bất cứ khi nào bạn xây dựng một ứng dụng web hoặc API bằng Node.js và Express/Connect, hãy nghĩ đến Helmet đầu tiên. Ứng dụng web có giao diện người dùng: Cực kỳ quan trọng để chống Clickjacking, XSS qua CSP, v.v. API backend: Dù không có giao diện, các headers như HSTS, noSniff vẫn quan trọng để bảo vệ endpoint của bạn. Ngay cả project cá nhân nhỏ: Tập thói quen tốt từ bây giờ để sau này không phải 'chữa cháy'. Nhớ nhé các bạn, bảo mật không phải là 'lựa chọn', mà là 'bắt buộc'. Hãy biến Helmet thành người bạn đồng hành không thể thiếu của mọi project Node.js của bạn. Đừng để app của mình 'trần truồng' trên internet nhé! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

37 Đọc tiếp
CORS: "Cảnh Sát Biên Giới" Cho API Node.js Của Bạn!
23/03/2026

CORS: "Cảnh Sát Biên Giới" Cho API Node.js Của Bạn!

Bắt Tay CORS: "Cảnh Sát Biên Giới" Cho API Node.js Của Bạn! Chào các chiến thần Gen Z! Hôm nay, thầy Creyt sẽ giải mã một trong những nỗi ám ảnh kinh hoàng nhất của dân dev frontend khi "bắt tay" với backend: lỗi CORS. Nghe tên đã thấy "khoai" rồi đúng không? Đừng lo, với package cors trong Node.js, chúng ta sẽ biến nỗi sợ hãi này thành món khai vị! 1. CORS là cái quái gì và để làm gì? Để dễ hình dung, các bạn cứ tưởng tượng thế này: Bạn là chủ một quán bar cực kỳ xịn xò (chính là cái API backend Node.js của bạn đấy). Khách hàng (là cái ứng dụng frontend bạn đang code, chạy trên React, Angular, Vue, hay thậm chí là một trang HTML đơn giản) muốn vào bar của bạn để gọi đồ (gửi request lấy dữ liệu). Nhưng mà, quán bar của bạn lại có một "cảnh sát biên giới" cực kỳ nghiêm ngặt (tên nó là Same-Origin Policy - SOP, chính sách bảo mật của trình duyệt). Ông cảnh sát này chỉ cho phép những vị khách đến từ "cùng một khu phố" (cùng một origin - tức là cùng giao thức, domain và port) vào bar thôi. Ví dụ, nếu website của bạn chạy ở https://app.example.com, thì nó chỉ được phép gọi API từ https://api.example.com hoặc https://app.example.com/api thôi. Nếu có một vị khách từ "khu phố khác" (ví dụ, frontend của bạn đang chạy ở http://localhost:3000 mà muốn gọi API ở http://localhost:5000, hoặc một website evil.com muốn gọi API của bạn) mà không có "giấy phép đặc biệt" thì sao? Ông cảnh sát sẽ "gào thét" lên một cái lỗi quen thuộc: "CORS Error!" và không cho vị khách đó vào. Vậy, CORS (Cross-Origin Resource Sharing) chính là cái "giấy phép đặc biệt" mà quán bar của bạn (server) cấp cho những vị khách "ngoại quốc" đó. Nó là một cơ chế bảo mật cho phép server kiểm soát xem những "khu phố" nào được phép truy cập tài nguyên của mình. Mục đích là để ngăn chặn các website độc hại truy cập dữ liệu của bạn mà không được phép. Package cors trong Node.js (thường dùng với Express.js) chính là "ông chủ quán bar" có kinh nghiệm, giúp bạn dễ dàng quản lý và cấp những "giấy phép" này một cách gọn gàng, an toàn, không cần phải tự mình viết luật lệ phức tạp. 2. Code Ví Dụ Minh Họa: Mở Cửa Quán Bar Một Cách Thông Minh Đầu tiên, bạn cần cài đặt cors và express: npm install cors express Giờ thì xem cách chúng ta "thuần hóa" nó: Ví dụ 1: Mở cửa cho tất cả (chỉ dùng khi dev thôi nhé!) Đây là cách nhanh nhất để "tắt" lỗi CORS khi đang phát triển. Giống như bạn mở toang cửa quán bar, ai vào cũng được. Thầy Creyt nhắc lại: CHỈ DÙNG KHI DEV THÔI! const express = require('express'); const cors = require('cors'); const app = express(); // Sử dụng cors middleware // Điều này cho phép MỌI origin truy cập API của bạn. // CỰC KỲ KHÔNG AN TOÀN TRONG PRODUCTION! app.use(cors()); // Một route ví dụ app.get('/data', (req, res) => { res.json({ message: 'Đây là dữ liệu từ server của bạn!' }); }); const PORT = process.env.PORT || 5000; app.listen(PORT, () => { console.log(`Server đang chạy trên cổng ${PORT}`); }); Ví dụ 2: Mở cửa có chọn lọc (Cách dùng chuẩn chỉ) Đây là cách mà thầy Creyt khuyên dùng trong hầu hết các trường hợp, đặc biệt là khi đưa lên production. Bạn chỉ cho phép những "khu phố" (origins) cụ thể vào bar của bạn thôi. const express = require('express'); const cors = require('cors'); const app = express(); // Cấu hình CORS để chỉ cho phép các origin cụ thể const corsOptions = { origin: ['http://localhost:3000', 'https://your-frontend-app.com'], // Danh sách các origin được phép methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', // Các HTTP methods được phép credentials: true, // Cho phép gửi cookies, authorization headers, v.v. optionsSuccessStatus: 204 // Một số trình duyệt cũ hơn có thể cần status này }; // Áp dụng cấu hình CORS app.use(cors(corsOptions)); // Một route ví dụ app.get('/secure-data', (req, res) => { // Giả sử bạn có middleware xác thực ở đây res.json({ message: 'Đây là dữ liệu bảo mật từ server!' }); }); const PORT = process.env.PORT || 5000; app.listen(PORT, () => { console.log(`Server đang chạy trên cổng ${PORT}`); }); Giải thích chi tiết các corsOptions: origin: Quan trọng nhất! Đây là danh sách các domain (hoặc một domain duy nhất dưới dạng string) mà bạn cho phép truy cập API. Nếu request đến từ một origin không có trong danh sách, nó sẽ bị chặn. methods: Các HTTP method (GET, POST, PUT, DELETE,...) mà các origin được phép sử dụng. Mặc định, cors cho phép tất cả các method tiêu chuẩn. credentials: Khi true, cho phép trình duyệt gửi cookie, HTTP authentication headers (ví dụ: Authorization) cùng với cross-origin request. Lưu ý: Nếu bạn đặt credentials: true, bạn không thể dùng origin: '*'. Phải chỉ định origin cụ thể! allowedHeaders: Các HTTP header mà client được phép gửi trong request cross-origin. Mặc định, một số header cơ bản đã được cho phép. exposedHeaders: Các HTTP header mà client được phép đọc từ response cross-origin (mặc định trình duyệt chỉ cho phép đọc một số header cơ bản). 3. Mẹo (Best Practices) để ghi nhớ và dùng thực tế Thầy Creyt có vài chiêu để các bạn "ăn điểm" với CORS: Đừng bao giờ app.use(cors()) trong production! (Trừ khi bạn là một hacker có đạo đức và đang thử nghiệm). Nó như mở toang cửa cho mọi người, kể cả trộm cướp vào nhà bạn. Hậu quả là dữ liệu của bạn có thể bị đánh cắp hoặc bị lợi dụng. Luôn chỉ định origin rõ ràng và cụ thể. Đây là "kim chỉ nam" để bảo vệ API của bạn. Chỉ cấp "giấy phép" cho những ai bạn tin tưởng. Hiểu về credentials: true: Dùng cái này khi bạn cần gửi cookie hoặc authorization token qua các request cross-origin (ví dụ, để duy trì trạng thái đăng nhập). Nhớ là khi bật credentials, origin bắt buộc phải là một domain cụ thể, không được dùng *. Preflight requests (OPTIONS): Khi client gửi một request "phức tạp" (ví dụ: dùng method khác GET/POST đơn giản, hoặc có custom header), trình duyệt sẽ tự động gửi một request OPTIONS trước (gọi là "preflight request") để hỏi server xem request chính có được phép hay không. Package cors tự động xử lý các preflight request này cho bạn, bạn không cần phải làm gì thêm. Thứ tự Middleware: Luôn đặt app.use(cors(corsOptions)) trước các route xử lý request của bạn. Nếu không, request có thể bị chặn bởi các route khác trước khi cors kịp xử lý. 4. Ví dụ thực tế các ứng dụng/website đã ứng dụng CORS không phải là thứ gì đó xa vời, nó hiện diện ở khắp mọi nơi bạn thấy frontend và backend "tách đôi": Các ứng dụng SPA (Single Page Applications): Hầu hết các ứng dụng React, Angular, Vue.js hiện đại đều chạy trên một domain riêng (ví dụ: app.yourcompany.com) và gọi API từ một domain khác (ví dụ: api.yourcompany.com). CORS là bắt buộc để chúng có thể giao tiếp được. Mobile Apps: Khi ứng dụng di động của bạn (iOS/Android) gọi đến một backend API, về lý thuyết thì không bị CORS vì mobile app không phải là trình duyệt. Tuy nhiên, khi bạn phát triển một Admin Panel hoặc Web Portal cho mobile app đó, bạn lại quay về câu chuyện CORS. Microservices: Nếu bạn có nhiều dịch vụ nhỏ giao tiếp với nhau, và một trong số chúng là một "cổng" (Gateway) mà frontend web gọi đến, thì CORS sẽ phát huy tác dụng ở đó. Ví dụ cụ thể: Bạn đang code một ứng dụng thương mại điện tử. Frontend của bạn chạy trên localhost:3000 (khi dev) hoặc shop.mycompany.com (khi deploy). Backend API của bạn chạy trên localhost:5000 (khi dev) hoặc api.mycompany.com (khi deploy). Khi frontend cố gắng fetch('/products') từ backend, nếu không có CORS, trình duyệt sẽ chặn đứng và báo lỗi. 5. Thử nghiệm và Hướng dẫn nên dùng cho case nào Trong môi trường Development (dev): Bạn có thể dùng app.use(cors()) một cách an toàn để "tắt" lỗi CORS và tập trung vào việc phát triển tính năng. Đừng quên thay đổi nó khi deploy nhé! Hoặc tốt hơn, dùng cors({ origin: 'http://localhost:3000' }) nếu frontend của bạn chạy trên cổng 3000. Trong môi trường Production (prod): BẮT BUỘC phải chỉ định origin cụ thể. Ví dụ: cors({ origin: 'https://your-frontend-app.com' }) hoặc một mảng các domain nếu bạn có nhiều frontend. Nếu bạn có nhiều subdomain, có thể dùng regex (nhưng cẩn thận). API công khai (Public API): Nếu bạn đang xây dựng một API mà bất kỳ ai cũng có thể sử dụng (ví dụ: API thời tiết, API dữ liệu công cộng), bạn có thể cân nhắc mở rộng origin hơn, thậm chí là * (nhưng vẫn nên cân nhắc kỹ về bảo mật, đặc biệt nếu có dữ liệu nhạy cảm). Thường thì các Public API sẽ yêu cầu API Key thay vì dựa vào CORS để bảo mật. API nội bộ/Microservices không qua trình duyệt: Nếu các dịch vụ backend của bạn giao tiếp với nhau mà không có trình duyệt nào liên quan, bạn không cần quan tâm đến CORS. CORS là một cơ chế bảo mật của trình duyệt, không phải của server. Nhớ nhé các bạn, CORS không phải là kẻ thù, nó là một "vệ sĩ" mà chúng ta cần biết cách điều khiển. Hiểu rõ và sử dụng đúng cors package sẽ giúp API của bạn vừa mạnh mẽ, vừa an toàn, và quan trọng nhất là không còn gây "đau đầu" cho frontend nữa! Cứ thế mà triển thôi! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

44 Đọc tiếp
Dọn dẹp bãi chiến trường lỗi: Error Handling Middleware trong Node.js
23/03/2026

Dọn dẹp bãi chiến trường lỗi: Error Handling Middleware trong Node.js

Chào các cháu Gen Z năng động của thầy Creyt! Hôm nay chúng ta sẽ cùng nhau 'khám phá' một vị anh hùng thầm lặng nhưng cực kỳ quan trọng trong thế giới Node.js: Error Handling Middleware. Nghe cái tên thì có vẻ hàn lâm, nhưng thầy đảm bảo nó 'chất' không kém gì mấy cái trend các cháu hay đu đâu! Tưởng tượng nha, ứng dụng của các cháu như một nhà hàng lớn, mỗi request là một khách hàng. Đôi khi, bếp núc (logic xử lý) gặp sự cố, món ăn bị cháy, hoặc nguyên liệu hết. Thay vì để khách hàng thấy cảnh hỗn loạn, Error Handling Middleware chính là đội ngũ quản lý khủng hoảng chuyên nghiệp, họ dọn dẹp bãi chiến trường, xin lỗi khách và đưa ra giải pháp (thông báo lỗi thân thiện). Đó chính là vai trò của nó! Khái Niệm - 'Đội Cứu Hỏa' Của Ứng Dụng Middleware, nói đơn giản, là những 'người gác cổng' đứng giữa request và response trong chu trình xử lý của ứng dụng. Chúng nó có quyền can thiệp, xử lý, hoặc chuyển tiếp request. Error Handling Middleware thì đặc biệt hơn, nó là 'đội cứu hỏa' chỉ xuất hiện khi có 'cháy' (lỗi) xảy ra. Điểm đặc trưng của Error Handling Middleware là nó nhận bốn tham số: (err, req, res, next). Cái err đó chính là 'đám cháy' mà các cháu quăng ra từ các route handler hay middleware khác bằng cách gọi next(err). Express sẽ tự động nhận diện hàm này là một error handler nhờ vào số lượng tham số đặc biệt này. Mục đích? Giúp ứng dụng không 'tạch' giữa chừng, trả về thông báo lỗi đẹp đẽ thay vì một màn hình trắng xóa hoặc lỗi 500 khó hiểu, và giúp các cháu dễ dàng debug hơn. Nó giúp cho trải nghiệm người dùng không bị 'tụt mood' và hệ thống của các cháu trông chuyên nghiệp hơn rất nhiều. Code Ví Dụ Minh Họa - Bắt 'Lỗi' Như Bắt 'Trend' Để các cháu dễ hình dung, thầy sẽ demo một ứng dụng Express.js nhỏ xíu, có một route 'dễ dãi' và một route 'hay dỗi' để các cháu thấy error handler hoạt động như thế nào khi có 'biến' nha. // app.js const express = require('express'); const app = express(); const PORT = 3000; // Middleware giả định có thể gây lỗi app.get('/api/data', (req, res, next) => { const shouldFail = Math.random() > 0.5; // 50% khả năng thất bại if (shouldFail) { // Ném một lỗi để middleware xử lý const error = new Error('Lỗi không tìm thấy dữ liệu hoặc lỗi nghiệp vụ nào đó!'); error.status = 404; // Tùy chỉnh status code cho lỗi này return next(error); // Chuyển lỗi cho error handling middleware } res.json({ message: 'Dữ liệu thành công rồi nè!' }); }); // Một route khác có thể ném lỗi đồng bộ (sẽ được Express tự động bắt và chuyển cho error handler) app.get('/api/broken', (req, res, next) => { // Ví dụ lỗi đồng bộ - không cần next(err) nếu không nằm trong async context throw new Error('Ứng dụng bị vỡ tan tành rồi, ai đó cứu tôi với!'); }); // Middleware xử lý 404 - Luôn đặt trước error handling cuối cùng app.use((req, res, next) => { const error = new Error('Đường dẫn này không tồn tại đâu nha Gen Z ơi!'); error.status = 404; next(error); // Chuyển lỗi 404 cho error handling middleware }); // Middleware xử lý lỗi TỔNG QUAN (PHẢI CÓ 4 THAM SỐ ĐỂ EXPRESS NHẬN DIỆN LÀ ERROR HANDLER) app.use((err, req, res, next) => { console.error('Ối giời ơi, có lỗi rồi:', err.message); // Log lỗi ra console server const statusCode = err.status || 500; // Mặc định là 500 nếu không có status cụ thể const message = err.message || 'Có gì đó sai sai ở server rồi, bình tĩnh nha!'; // Trong môi trường production, KHÔNG NÊN gửi chi tiết lỗi ra ngoài // Để tránh lộ thông tin nhạy cảm. Thầy Creyt nhắc rồi đó! const responseError = { status: statusCode, message: statusCode === 500 ? 'Lỗi hệ thống nội bộ, liên hệ quản trị viên!' : message, // Chỉ gửi stack trace trong môi trường phát triển để debug stack: process.env.NODE_ENV === 'development' ? err.stack : undefined }; res.status(statusCode).json(responseError); }); app.listen(PORT, () => { console.log(`Server của thầy Creyt đang chạy ở http://localhost:${PORT}`); }); Cách chạy: Tạo một thư mục mới, ví dụ error-demo. cd error-demo npm init -y npm install express Tạo file app.js và copy đoạn code trên vào. node app.js Giờ thì mở trình duyệt hoặc Postman/Insomnia, thử truy cập: http://localhost:3000/api/data (thử vài lần để thấy lỗi 404) http://localhost:3000/api/broken (để thấy lỗi 500 do throw) http://localhost:3000/api/non-existent (để thấy lỗi 404 từ middleware xử lý đường dẫn không tồn tại) Các cháu sẽ thấy server không 'sập', mà trả về JSON lỗi đẹp đẽ, đúng chuẩn API. Mẹo Hay Từ Thầy Creyt - 'Bí Kíp Võ Lâm' Để 'Hack' Lỗi Vị trí là tất cả: Luôn đặt error handling middleware cuối cùng trong chuỗi middleware và route của ứng dụng. Nó như người gác cổng cuối cùng vậy đó, mọi thứ không ai xử lý được thì nó 'ôm' hết. next(err) là chìa khóa: Khi muốn chuyển lỗi đến error handler, hãy dùng next(err). Đặc biệt với các hàm async/await trong route handler, hãy luôn try...catch và gọi next(err) trong catch block. Nếu không, lỗi có thể không được bắt kịp và làm 'sập' ứng dụng. Phân loại lỗi: Chia lỗi thành Operational errors (lỗi do người dùng, ví dụ: dữ liệu không hợp lệ, không tìm thấy tài nguyên - thường là 4xx) và Programmer errors (lỗi do dev, ví dụ: bug trong code, lỗi kết nối DB - thường là 500). Xử lý mỗi loại một cách khác nhau để trả về phản hồi phù hợp và log hiệu quả hơn. Log lỗi nghiêm túc: Đừng chỉ console.error thôi nha! Dùng các thư viện logging chuyên nghiệp như Winston hay Pino để log lỗi ra file hoặc dịch vụ log tập trung. Điều này cực kỳ quan trọng cho việc debug và giám sát ứng dụng trong production. Thân thiện với người dùng (và bảo mật): Ở môi trường production, đừng bao giờ trả về stack trace hay thông tin lỗi chi tiết cho client. Chỉ trả về một thông báo chung chung, thân thiện và không tiết lộ cấu trúc nội bộ của server. Thông tin chi tiết chỉ nên ở server logs. Sử dụng thư viện hỗ trợ async: Để không phải bọc mọi async route trong try...catch một cách thủ công, các cháu có thể dùng express-async-errors hoặc express-promise-router. Chúng sẽ tự động bắt các lỗi từ Promise bị reject và chuyển cho error handler. Ứng Dụng Thực Tế - 'Sống Còn' Của Mọi App Lớn Các cháu dùng Shopee, Facebook, TikTok, hay bất kỳ app nào. Khi mạng yếu, dữ liệu không tải được, hoặc có lỗi server, app không 'đơ' mà thường hiển thị thông báo 'Đã có lỗi xảy ra, vui lòng thử lại sau' hoặc 'Không tìm thấy sản phẩm'. Đó chính là thành quả của error handling middleware hoạt động hiệu quả đó. Nó giúp duy trì trải nghiệm người dùng, dù có lỗi nhưng app vẫn 'sống', không 'chết lâm sàng'. Thử Nghiệm & Nên Dùng Khi Nào - 'Đòn Bẩy' Của Dev Chuyên Nghiệp Thử nghiệm: Đừng ngại ngần! Tự tạo ra các lỗi khác nhau: lỗi 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 404 (Not Found), 500 (Internal Server Error). Xem error handler của các cháu phản ứng thế nào. Thậm chí hãy tạo ra các Custom Error Classes để phân loại lỗi nghiệp vụ rõ ràng hơn. Nên dùng khi nào? Mọi lúc! Bất cứ khi nào các cháu xây dựng một API server bằng Node.js và Express, error handling middleware là một phần không thể thiếu. Nó là 'áo giáp' bảo vệ ứng dụng của các cháu khỏi những cú 'đấm' bất ngờ, giúp ứng dụng của các cháu ổn định và đáng tin cậy hơn rất nhiều. Đừng đợi đến khi sản phẩm 'lên sóng' rồi mới cuống cuồng vá víu. Hãy tích hợp nó ngay từ đầu! Vậy đó, Error Handling Middleware không chỉ là một khái niệm kỹ thuật khô khan, mà là một 'nghệ thuật' giúp ứng dụng của các cháu trở nên mạnh mẽ, bền bỉ và chuyên nghiệp hơn. Hãy luyện tập thật nhiều và biến nó thành kỹ năng 'sát thủ' của mình nha! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

53 Đọc tiếp
Middleware: Cửa Ải Quyền Năng Của Node.js
23/03/2026

Middleware: Cửa Ải Quyền Năng Của Node.js

Chào các đệ tử công nghệ, anh Creyt đây! Hôm nay, chúng ta sẽ "bóc phốt" một khái niệm mà nếu không hiểu rõ, app của mấy đứa sẽ như một cái nhà không có hàng rào, ai muốn vào làm gì cũng được. Đó chính là Middleware Functions trong Node.js, cụ thể hơn là với Express.js – framework "quốc dân" cho anh em mình. Middleware là gì mà nghe ngầu vậy anh? Nói một cách dễ hiểu, mấy đứa cứ hình dung cái app Node.js của mình là một cái "club" xịn sò. Mỗi khi có một "khách hàng" (request HTTP) muốn vào club để "quẩy" (xử lý logic), họ không thể đi thẳng vào sàn nhảy (route handler) ngay được. Thay vào đó, họ phải đi qua một loạt các "cửa ải kiểm soát" hay còn gọi là Middleware. Mỗi "cửa ải" này là một hàm JavaScript, có nhiệm vụ: Kiểm tra, soi xét: "Khách" này là ai? Đến từ đâu? Có đủ điều kiện vào không? Xử lý, thay đổi: Có cần ghi lại thông tin của "khách" không? Có cần "thay đồ" cho "khách" trước khi vào không? (Ví dụ: parse JSON body). Cho qua hoặc chặn đứng: Nếu "khách" đủ điều kiện, cho qua cửa ải tiếp theo. Nếu không, "tống cổ" ra khỏi club luôn (gửi lỗi về client). Tóm lại, Middleware là những hàm trung gian chạy trước khi request đến được hàm xử lý chính (route handler). Chúng có quyền truy cập vào đối tượng request (req), response (res), và cái quan trọng nhất: hàm next() – "chìa khóa" để cho request đi tiếp. Để làm gì? Sao không code thẳng vào route handler cho rồi? À há! Câu hỏi hay đấy! Nếu mấy đứa code tất cả logic kiểm tra, xác thực, ghi log... vào từng route handler, thì: Code sẽ dài như sớ Táo Quân: Mỗi handler vài trăm dòng, nhìn vào muốn "tụt mood" code. Dễ lặp lại code: Đoạn code kiểm tra token xác thực, ghi log... sẽ phải copy paste khắp nơi. Ôi giời ơi, maintenance thì "toang". Khó mở rộng, khó quản lý: Muốn thêm một bước kiểm tra mới? Lại phải sửa hàng chục chỗ. Middleware giải quyết tất cả những vấn đề này bằng cách tạo ra một "chuỗi chuyền" xử lý request. Mỗi "mắt xích" trong chuỗi chỉ làm một nhiệm vụ cụ thể, giúp code sạch, dễ đọc, dễ bảo trì và tái sử dụng. Nó giống như việc bạn có một đội ngũ bảo vệ, soát vé, kiểm tra tuổi riêng biệt thay vì một anh chàng đa năng làm tất cả mọi thứ vậy. Code Ví Dụ Minh Hoạ: Anh em mình cùng "xây club" Để mấy đứa dễ hình dung, anh sẽ "xây" một cái club đơn giản với Express.js và vài cái cửa ải middleware. Đầu tiên, đảm bảo mấy đứa đã npm install express nhé. // server.js const express = require('express'); const app = express(); const PORT = 3000; // --- 1. Middleware ghi log (Anh bảo vệ ghi sổ) --- const loggerMiddleware = (req, res, next) => { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] Request: ${req.method} ${req.originalUrl}`); // Quan trọng: Gọi next() để request đi tiếp đến middleware hoặc route handler kế tiếp next(); }; // --- 2. Middleware xác thực (Anh soát vé kiểm tra) --- const authMiddleware = (req, res, next) => { // Giả định: Kiểm tra header 'Authorization' có chứa token 'SECRET_TOKEN' const authorizationHeader = req.headers.authorization; if (authorizationHeader && authorizationHeader === 'Bearer SECRET_TOKEN') { console.log('Xác thực thành công!'); // Nếu OK, cho qua cửa ải tiếp theo next(); } else { console.log('Xác thực thất bại!'); // Nếu không OK, chặn đứng và gửi lỗi về client res.status(401).send('Unauthorized: Vui lòng cung cấp token hợp lệ!'); } }; // --- 3. Áp dụng Middleware --- // app.use() dùng để áp dụng middleware cho TẤT CẢ các request đến server app.use(loggerMiddleware); // Mọi request đều đi qua anh bảo vệ ghi log trước // Tuyến đường cần xác thực (chỉ những ai có token mới được vào) app.get('/dashboard', authMiddleware, (req, res) => { res.send('Chào mừng bạn đến với Dashboard bí mật! Bạn đã được xác thực.'); }); // Tuyến đường công khai (không cần xác thực) app.get('/', (req, res) => { res.send('Chào mừng bạn đến với trang chủ công khai.'); }); // --- 4. Middleware xử lý lỗi (Anh cấp cứu khi có sự cố) --- // Middleware xử lý lỗi luôn có 4 tham số: (err, req, res, next) app.use((err, req, res, next) => { console.error('Có lỗi xảy ra:', err.stack); res.status(500).send('Something broke! Có lỗi gì đó rồi bạn ơi!'); }); // Khởi động server app.listen(PORT, () => { console.log(`Server đang chạy ở http://localhost:${PORT}`); }); Cách chạy thử: Mở terminal, chạy node server.js. Mở trình duyệt hoặc Postman/Insomnia: Truy cập http://localhost:3000/: Sẽ thấy log ghi lại và trang chủ hiện ra. Truy cập http://localhost:3000/dashboard: Sẽ bị báo lỗi "Unauthorized". Dùng Postman/Insomnia, thêm header Authorization với giá trị Bearer SECRET_TOKEN khi gửi request đến /dashboard: Bạn sẽ vào được dashboard. Thấy chưa? Mỗi middleware làm đúng một nhiệm vụ, rồi next() để request đi tiếp. Nếu không có next(), request sẽ bị "treo" ở middleware đó luôn, không bao giờ đến được route handler! Mẹo và Best Practices từ anh Creyt (Để code không "toang"!) Luôn nhớ next(): Đây là "chìa khóa" thần thánh. Quên nó là request của mấy đứa "tắc tị" ngay tại middleware đó, không đi đâu được. Trừ khi middleware đó quyết định kết thúc request bằng cách gửi response (như res.status(401).send(...) trong ví dụ auth). Thứ tự là Vua: Middleware được thực thi theo thứ tự mà mấy đứa app.use() chúng. Ví dụ, middleware ghi log nên đứng đầu để ghi lại mọi thứ. Middleware xác thực phải đứng trước các route cần bảo vệ. Single Responsibility Principle: Mỗi middleware chỉ nên làm MỘT nhiệm vụ duy nhất. Đừng cố nhồi nhét quá nhiều logic vào một middleware. Một anh bảo vệ chỉ nên bảo vệ, không nên vừa bảo vệ vừa bán vé vừa pha chế cocktail. Sử dụng req và res để truyền dữ liệu: Middleware có thể thêm thuộc tính vào đối tượng req để truyền dữ liệu cho các middleware hoặc route handler phía sau. Ví dụ, sau khi xác thực, bạn có thể thêm req.user = decodedToken.user; để các handler sau biết ai đang request. Middleware xử lý lỗi (Error Handling Middleware): Luôn đặt chúng ở cuối cùng trong chuỗi app.use(). Chúng có 4 tham số (err, req, res, next) và sẽ "tóm" những lỗi ném ra từ các middleware hoặc route handler phía trước. Ứng dụng thực tế: Middleware "cân" cả thế giới web Mấy đứa đang dùng rất nhiều app, website mà không biết rằng chúng đang "chạy" trên middleware đấy: Facebook, Instagram, TikTok: Khi mấy đứa đăng nhập, đó là middleware xác thực đang hoạt động. Khi upload ảnh, middleware có thể kiểm tra định dạng, kích thước ảnh. Shopee, Tiki, Lazada: Khi thêm sản phẩm vào giỏ hàng, middleware có thể kiểm tra tồn kho. Khi thanh toán, middleware xử lý giao dịch, kiểm tra thông tin thanh toán. API của bất kỳ ứng dụng nào: Logging: Ghi lại mọi request để debug, phân tích. Authentication/Authorization: Đảm bảo chỉ người dùng có quyền mới truy cập được các tài nguyên nhạy cảm. Body Parsing: Chuyển đổi dữ liệu JSON/form-urlencoded từ client thành object JavaScript dễ dùng (express.json(), express.urlencoded()). CORS (Cross-Origin Resource Sharing): Cho phép hoặc chặn các request từ các domain khác nhau. Rate Limiting: Hạn chế số lượng request từ một IP nào đó để chống spam, DDoS. Compression: Nén dữ liệu response để gửi về client nhanh hơn (compression middleware). Thử nghiệm và Nên dùng cho case nào? (Kinh nghiệm xương máu của anh Creyt) Anh Creyt đã từng "sấp mặt" khi mới tập tành với middleware. Hồi đó, anh hay quên next(), làm app cứ "đứng hình" không báo lỗi gì cả, mò debug mãi mới ra. Hoặc nhồi nhét quá nhiều thứ vào một middleware, đến lúc sửa thì như "gỡ rối tơ vò". Nên dùng Middleware khi: Cần thực hiện một tác vụ chung cho nhiều route: Ví dụ: ghi log, xác thực, kiểm tra quyền, parse body. Muốn tách biệt các "mối quan tâm" (separation of concerns): Mỗi middleware làm một việc, giúp code modular, dễ đọc, dễ kiểm thử. Cần can thiệp vào request/response trước hoặc sau khi xử lý chính: Thay đổi header, thêm dữ liệu vào req, xử lý lỗi tập trung. Xây dựng API bảo mật: Xác thực token, kiểm tra vai trò người dùng. Khi nào thì không nên lạm dụng? Khi tác vụ chỉ liên quan đến một route duy nhất và rất đơn giản: Đôi khi, một đoạn code nhỏ trong chính route handler lại dễ đọc hơn là tạo một middleware riêng. Khi logic quá phức tạp và cần nhiều trạng thái: Lúc đó có thể cân nhắc dùng service hoặc controller thay vì middleware để giữ cho middleware gọn gàng. Tóm lại, middleware là một "công cụ quyền năng" giúp mấy đứa xây dựng các ứng dụng Node.js/Express.js mạnh mẽ, có tổ chức và dễ bảo trì hơn rất nhiều. Hãy làm chủ nó, và mấy đứa sẽ thấy việc phát triển backend trở nên "dễ thở" hơn bao giờ hết! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

36 Đọc tiếp
Giải mã req.body: Túi thần kỳ của dữ liệu Node.js
23/03/2026

Giải mã req.body: Túi thần kỳ của dữ liệu Node.js

Chào các genZ developer tương lai, hôm nay anh Creyt sẽ "bóc phốt" một khái niệm mà các em sẽ gặp như cơm bữa khi làm backend với Node.js và Express: req.body. Nghe có vẻ khô khan nhưng anh hứa sẽ làm nó dễ hiểu như cách các em lướt TikTok vậy! req.body là gì? "Hộp quà" bí ẩn từ Client Để anh Creyt kể cho nghe, trong thế giới web, khi client (trình duyệt web, ứng dụng di động của mấy đứa) muốn gửi dữ liệu lên server của mình, có nhiều cách. Có thể là gửi qua URL (req.query), gửi qua đường dẫn (req.params), nhưng những cách đó thường chỉ để gửi dữ liệu nhỏ, công khai, hoặc để định danh thôi. Còn khi mấy đứa muốn gửi một "gói hàng" lớn hơn, phức tạp hơn, hoặc "nhạy cảm" hơn – ví dụ như tên đăng nhập và mật khẩu, nội dung một bài post dài ngoằng, hay cả một object sản phẩm với ti tỉ thuộc tính – thì đó chính là lúc req.body vào vai. req.body chính là cái "hộp quà đóng gói kỹ lưỡng" mà client gửi kèm theo các yêu cầu HTTP như POST, PUT, PATCH. Nó chứa toàn bộ dữ liệu mà client muốn "nhét" vào yêu cầu để server xử lý. Dữ liệu này không hiển thị trên URL, giúp giữ cho URL "sạch sẽ" và bảo mật hơn cho các thông tin nhạy cảm. Để làm gì? Khi bạn muốn Server "nuốt" dữ liệu lớn Đơn giản là để server của bạn có thể nhận và xử lý dữ liệu được gửi từ client một cách "kín đáo" và có cấu trúc. Tưởng tượng bạn đi siêu thị mua đồ, req.query là cái list ghi chú dán bên ngoài túi, còn req.params là cái tem dán mã vạch. req.body chính là tất cả những món đồ thật sự bên trong cái túi đó. Server cần biết bên trong có gì để biết cách sắp xếp, chế biến. "Người bóc quà" - Middleware À mà khoan, server không tự nhiên mà hiểu được "ngôn ngữ" của cái hộp quà đâu nha. Dữ liệu trong req.body thường được đóng gói dưới nhiều định dạng khác nhau như JSON (application/json) hoặc form URL-encoded (application/x-www-form-urlencoded). Server của bạn cần một "người giải mã" hay "người bóc quà" chuyên nghiệp để mở cái hộp đó ra và biến nó thành một object JavaScript mà bạn có thể dễ dàng làm việc. Trong Express.js, những "người bóc quà" này chính là các middleware như express.json() và express.urlencoded(). Nếu không có chúng, req.body của bạn sẽ chỉ là undefined hoặc một object rỗng tuếch, server sẽ "đứng hình" không biết client đang gửi cái gì đâu. Code Ví Dụ Minh Hoạ: Bóc quà cùng Creyt Đây, anh Creyt sẽ cho mấy đứa thấy cách server "bóc quà" như thế nào: Đầu tiên, cài đặt Express và tạo file app.js: npm init -y npm install express app.js (Server-side): const express = require('express'); const app = express(); const port = 3000; // Middleware "bóc quà" JSON app.use(express.json()); // Middleware "bóc quà" Form URL-encoded // { extended: true } cho phép parse các object và array lồng nhau app.use(express.urlencoded({ extended: true })); // Route xử lý POST request để tạo một bài viết mới app.post('/posts', (req, res) => { console.log('Dữ liệu từ req.body:', req.body); // Ví dụ dữ liệu mong muốn từ client: // { title: "Tiêu đề bài viết", content: "Nội dung chi tiết...", author: "Creyt" } const { title, content, author } = req.body; if (!title || !content) { return res.status(400).json({ message: 'Tiêu đề và nội dung không được để trống!' }); } // Ở đây, mấy đứa sẽ lưu dữ liệu vào database thật // Tạm thời, chúng ta chỉ gửi lại xác nhận const newPost = { id: Math.floor(Math.random() * 1000), title, content, author: author || 'Anonymous', createdAt: new Date() }; res.status(201).json({ message: 'Bài viết đã được tạo thành công!', post: newPost }); }); // Route xử lý PUT request để cập nhật thông tin người dùng app.put('/users/:id', (req, res) => { const userId = req.params.id; const { name, email, bio } = req.body; console.log(`Cập nhật người dùng ${userId} với dữ liệu:`, req.body); if (!name || !email) { return res.status(400).json({ message: 'Tên và email không được để trống!' }); } // Logic cập nhật database ở đây const updatedUser = { id: userId, name, email, bio: bio || 'No bio provided', updatedAt: new Date() }; res.json({ message: `Người dùng ${userId} đã được cập nhật thành công!`, user: updatedUser }); }); app.listen(port, () => { console.log(`Server đang chạy tại http://localhost:${port}`); }); Cách gửi dữ liệu từ Client (Dùng curl hoặc fetch): 1. Gửi dữ liệu JSON (thường dùng cho API): curl -X POST -H "Content-Type: application/json" \ -d '{"title": "Bài viết đầu tiên của GenZ", "content": "Nội dung cực chất về req.body", "author": "Creyt"}' \ http://localhost:3000/posts 2. Gửi dữ liệu Form URL-encoded (thường dùng cho form HTML truyền thống): curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \ -d "title=Bài+viết+thứ+hai&content=Nội+dung+dạng+form" \ http://localhost:3000/posts 3. Gửi PUT request để cập nhật: curl -X PUT -H "Content-Type: application/json" \ -d '{"name": "Creyt Dev", "email": "creyt@example.com", "bio": "Giảng viên lập trình dí dỏm"}' \ http://localhost:3000/users/123 Khi chạy các lệnh curl này, mấy đứa sẽ thấy server in ra req.body trong console và trả về phản hồi tương ứng. Tuyệt vời chưa! Mẹo & Best Practices (Công thức của Creyt) Đừng bao giờ quên Middleware: Đây là điều tối quan trọng! Nếu mấy đứa muốn req.body có dữ liệu, hãy luôn thêm app.use(express.json()); và app.use(express.urlencoded({ extended: true })); vào đầu file server của mình (trước các route). Không có chúng, req.body sẽ là undefined và mấy đứa sẽ tốn cả buổi debug đấy. "Đừng tin bất cứ thứ gì đến từ client": Đây là câu thần chú của anh Creyt. Dữ liệu từ req.body luôn cần được xác thực (validate) và làm sạch (sanitize). Client có thể gửi bất cứ thứ gì, từ dữ liệu thiếu, sai định dạng đến các đoạn mã độc. Luôn kiểm tra xem title có rỗng không, email có đúng định dạng không, v.v. trước khi xử lý hoặc lưu vào database. (Hãy dùng thư viện như Joi hoặc express-validator). Hiểu Content-Type: Client gửi Content-Type nào thì server cần middleware tương ứng để parse. express.json() cho application/json, express.urlencoded() cho application/x-www-form-urlencoded. Nếu client gửi dạng multipart/form-data (thường dùng để upload file), mấy đứa sẽ cần thư viện khác như multer. Bảo mật là trên hết: Dữ liệu từ req.body là cửa ngõ tiềm năng cho các cuộc tấn công như XSS (Cross-Site Scripting) hay SQL Injection. Luôn đảm bảo dữ liệu được làm sạch kỹ lưỡng trước khi hiển thị lại cho người dùng hoặc đưa vào truy vấn database. Ứng dụng thực tế: req.body có mặt khắp nơi req.body được sử dụng trong hầu hết các ứng dụng web và API mà mấy đứa dùng hàng ngày: Đăng nhập/Đăng ký: Khi mấy đứa điền username và password vào form đăng nhập, dữ liệu đó được gửi lên server qua req.body. Tạo bài viết/sản phẩm: Khi mấy đứa tạo một bài post trên Facebook, đăng một sản phẩm mới trên Shopee, hoặc viết một email, toàn bộ nội dung đó đều nằm trong req.body. Cập nhật thông tin cá nhân: Thay đổi avatar, cập nhật địa chỉ, số điện thoại trong phần cài đặt tài khoản. API cho ứng dụng di động: Các app mobile gửi và nhận dữ liệu từ backend thông qua các API, và req.body là cách chính để gửi dữ liệu phức tạp. Thử nghiệm & Nên dùng cho Case nào Anh Creyt đã từng thử không dùng middleware để xem req.body nó "trống rỗng" như thế nào rồi, và kết quả là undefined thật! Từ đó anh mới thấm thía tầm quan trọng của mấy cái app.use() này. Vậy khi nào thì nên dùng req.body? Khi cần gửi dữ liệu "nặng" hoặc "nhạy cảm": Những thông tin không nên lộ trên URL. Khi dữ liệu có cấu trúc phức tạp: JSON object, array of objects, v.v. Với các phương thức HTTP POST, PUT, PATCH: Đây là những phương thức được thiết kế để gửi dữ liệu lên server để tạo mới, cập nhật hoặc thay đổi tài nguyên. Phân biệt nhanh: req.query: Dữ liệu nhỏ, không nhạy cảm, dùng để lọc, tìm kiếm, phân trang (ví dụ: /products?category=electronics&page=1). req.params: Dùng để xác định tài nguyên cụ thể (ví dụ: /products/123 để lấy sản phẩm có ID là 123). req.body: Dữ liệu lớn, cấu trúc phức tạp, nhạy cảm, dùng để tạo mới, cập nhật tài nguyên. Hy vọng qua bài này, mấy đứa đã "khai sáng" được về req.body và biết cách sử dụng nó một cách hiệu quả và an toàn. Nhớ những lời anh Creyt dặn nhé: "Đừng quên middleware và đừng tin client!" Chúc mấy đứa code vui vẻ! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

42 Đọc tiếp
req.query: Cảnh Sát Giao Thông Của URL, GenZ Ơi!
23/03/2026

req.query: Cảnh Sát Giao Thông Của URL, GenZ Ơi!

Chào các "thánh code" GenZ của anh Creyt! Hôm nay, chúng ta sẽ "đập hộp" một khái niệm nghe thì có vẻ "hàn lâm" nhưng thực ra lại "cool ngầu" và cực kỳ thiết yếu trong thế giới backend Node.js: req.query. Nghe quen mà lạ đúng không? Đừng lo, anh Creyt sẽ "giải mã" nó theo cách dễ hiểu nhất, đảm bảo "nuốt trôi" liền! 1. req.query là gì mà "hot" thế? Để dễ hình dung, các em cứ tưởng tượng thế này: Một cái URL (Uniform Resource Locator) giống như một cái địa chỉ nhà trên đường cao tốc Internet vậy. Ví dụ, https://shopee.vn/search?keyword=ao+thun&category=123&minPrice=50000&maxPrice=100000. Cái phần https://shopee.vn/search là con đường chính, là địa chỉ mà các em muốn tới. Còn cái phần sau dấu chấm hỏi ? ấy, chính là "tờ giấy note" mà các em dán lên gói hàng trước khi gửi đi. Nó ghi những yêu cầu đặc biệt, những thông tin phụ trợ mà các em muốn người nhận (server) biết để xử lý gói hàng (request) của mình tốt hơn. req.query chính là cái "hộp" chứa tất cả những "tờ giấy note" đó! Trong Node.js (cụ thể là với framework Express.js), khi một request HTTP đến server của bạn, req.query là một đối tượng (object) mà Express tự động phân tích và điền vào. Nó chứa các cặp key-value lấy từ chuỗi truy vấn (query string) của URL. Ví dụ: Với URL http://localhost:3000/api/products?category=Thời%20trang&price_max=300000 Thì req.query trong code của bạn sẽ trông như thế này: { category: 'Thời trang', price_max: '300000' } Dễ hiểu không? Nó là cách client (trình duyệt, ứng dụng di động) "thì thầm" những yêu cầu tùy chọn cho server mà không làm thay đổi cái địa chỉ chính của tài nguyên. 2. Dùng req.query để làm gì? "Tờ giấy note" này có "phép thuật" ghê gớm lắm đó, GenZ ạ! Nó thường được dùng cho các trường hợp: Lọc dữ liệu (Filtering): Muốn xem tất cả sản phẩm thuộc danh mục "Điện thoại"? ?category=dien_thoai. Sắp xếp dữ liệu (Sorting): Muốn sắp xếp bài viết theo ngày mới nhất hay lượt xem cao nhất? ?sort_by=date_desc. Phân trang (Pagination): Muốn lấy trang thứ 2, mỗi trang 10 mục? ?page=2&limit=10. Tìm kiếm (Searching): Tìm kiếm sản phẩm có tên "áo thun"? ?search=ao+thun. Tham số tùy chọn (Optional parameters): Đôi khi bạn muốn server trả về dữ liệu theo một định dạng nhất định, hoặc hiển thị một ngôn ngữ khác. ?lang=en. Nói chung, bất cứ khi nào bạn cần truyền các tham số tùy chọn, không ảnh hưởng đến việc định danh tài nguyên chính, và không quá nhạy cảm (vì nó hiện rõ trên URL), thì req.query chính là "người hùng" của bạn. 3. Code Ví Dụ Minh Họa (Chất Lượng Cao) Anh Creyt sẽ dựng một server Express "mini" để các em thấy req.query hoạt động như thế nào trong việc lọc và sắp xếp danh sách sản phẩm nhé! const express = require('express'); const app = express(); const PORT = 3000; // Một "database" nhỏ xíu để minh họa const products = [ { id: 1, name: 'Áo thun GenZ', category: 'Thời trang', price: 199000, color: 'đen' }, { id: 2, name: 'Quần jean rách', category: 'Thời trang', price: 350000, color: 'xanh' }, { id: 3, name: 'Tai nghe Bluetooth', category: 'Phụ kiện', price: 800000, color: 'trắng' }, { id: 4, name: 'Sạc dự phòng 10000mAh', category: 'Phụ kiện', price: 250000, color: 'đen' }, { id: 5, name: 'Sách "Lập trình siêu ngầu"', category: 'Sách', price: 120000, color: 'nhiều màu' }, { id: 6, name: 'Balo laptop gaming', category: 'Phụ kiện', price: 700000, color: 'đen' } ]; // Route để lấy danh sách sản phẩm với các tùy chọn lọc/sắp xếp từ req.query app.get('/api/products', (req, res) => { console.log('Các yêu cầu đặc biệt từ GenZ (req.query):', req.query); let filteredProducts = [...products]; // Tạo bản sao để lọc, tránh làm thay đổi dữ liệu gốc const { category, price_max, sort_by, search } = req.query; // Bóc tách các tham số từ req.query // 1. Lọc theo category (danh mục) if (category) { filteredProducts = filteredProducts.filter(p => p.category.toLowerCase() === category.toLowerCase() ); } // 2. Lọc theo giá tối đa if (price_max) { const maxPrice = parseFloat(price_max); // Chuyển đổi từ string sang số thực if (!isNaN(maxPrice)) { // Đảm bảo đây là một số hợp lệ filteredProducts = filteredProducts.filter(p => p.price <= maxPrice); } else { return res.status(400).json({ message: 'Giá tối đa không hợp lệ, GenZ ơi!' }); } } // 3. Tìm kiếm theo tên sản phẩm if (search) { filteredProducts = filteredProducts.filter(p => p.name.toLowerCase().includes(search.toLowerCase()) ); } // 4. Sắp xếp if (sort_by) { switch (sort_by) { case 'price_asc': // Giá tăng dần filteredProducts.sort((a, b) => a.price - b.price); break; case 'price_desc': // Giá giảm dần filteredProducts.sort((a, b) => b.price - a.price); break; case 'name_asc': // Tên tăng dần (theo alphabet) filteredProducts.sort((a, b) => a.name.localeCompare(b.name)); break; default: // Có thể bỏ qua hoặc trả về lỗi nếu sort_by không hợp lệ break; } } // Nếu không tìm thấy sản phẩm nào if (filteredProducts.length === 0) { return res.status(404).json({ message: 'Không tìm thấy sản phẩm nào theo yêu cầu của bạn, GenZ ạ!' }); } res.json({ message: 'Đây là danh sách sản phẩm theo yêu cầu đặc biệt của bạn:', count: filteredProducts.length, data: filteredProducts }); }); app.listen(PORT, () => { console.log(`Server của Creyt đang chạy ở http://localhost:${PORT}`); console.log('Thử nghiệm với các URL sau:'); console.log(`- Lấy tất cả sản phẩm: http://localhost:${PORT}/api/products`); console.log(`- Lọc theo category "Thời trang": http://localhost:${PORT}/api/products?category=Thời%20trang`); console.log(`- Lọc theo giá tối đa 300k: http://localhost:${PORT}/api/products?price_max=300000`); console.log(`- Lọc theo category và giá: http://localhost:${PORT}/api/products?category=Thời%20trang&price_max=200000`); console.log(`- Tìm kiếm "thun" và sắp xếp giá tăng dần: http://localhost:${PORT}/api/products?search=thun&sort_by=price_asc`); console.log(`- Lọc phụ kiện và sắp xếp giá giảm dần: http://localhost:${PORT}/api/products?category=Phụ%20kiện&sort_by=price_desc`); }); Cách chạy: Lưu đoạn code trên vào một file app.js. Mở Terminal/CMD, chạy npm init -y (nếu chưa có package.json). Cài Express: npm install express. Chạy server: node app.js. Mở trình duyệt và thử các URL mà anh Creyt đã gợi ý trong console.log. 4. Mẹo (Best Practices) của Creyt để "cân" req.query Là một "giảng viên lập trình lão luyện" như anh Creyt, anh đã "ăn hành" đủ nhiều để đúc kết ra vài mẹo xương máu cho các em đây: "Đừng tin ai cả!" (Validate và Sanitize): Đây là điều quan trọng nhất! Dữ liệu từ req.query (hay bất kỳ input nào từ người dùng) luôn phải được kiểm tra (validate) và làm sạch (sanitize) trước khi sử dụng. Như trong ví dụ, anh đã dùng parseFloat và isNaN để đảm bảo price_max là một số hợp lệ. Nếu không, hacker có thể gửi những chuỗi quái dị để làm sập server hoặc thậm chí là tấn công SQL Injection (nếu bạn dùng trực tiếp vào database query). Giá trị mặc định (Default Values): Client không phải lúc nào cũng gửi đủ các tham số. Hãy cung cấp giá trị mặc định cho các tham số tùy chọn. Ví dụ: const page = parseInt(req.query.page) || 1; (mặc định trang 1). Nhất quán tên (Consistent Naming): Dùng snake_case (price_max) hoặc camelCase (priceMax) một cách nhất quán cho tên các query parameter để dễ đọc và dễ quản lý hơn. Giới hạn độ dài URL: Mặc dù req.query tiện lợi, nhưng URL có giới hạn độ dài nhất định (thường là vài nghìn ký tự tùy trình duyệt/server). Đừng cố nhồi nhét quá nhiều dữ liệu vào đây. Nếu dữ liệu quá lớn hoặc nhạy cảm, hãy dùng req.body với các request POST/PUT. Sử dụng thư viện hỗ trợ: Đối với các ứng dụng phức tạp, có nhiều thư viện giúp validate và sanitize dữ liệu hiệu quả hơn, ví dụ express-validator. 5. Ứng dụng thực tế: req.query "làm mưa làm gió" ở đâu? Chắc chắn các em đã dùng req.query hàng ngày mà không biết đấy thôi: Google Search: Khi các em gõ "req.query nodejs" vào Google, URL sẽ thành https://www.google.com/search?q=req.query+nodejs. Cái q=req.query+nodejs chính là req.query đó! Các trang thương mại điện tử (Shopee, Tiki, Lazada): Khi các em lọc sản phẩm theo giá, màu sắc, danh mục, sắp xếp theo lượt bán chạy, hay chuyển trang, tất cả đều dùng req.query. Ví dụ: https://shopee.vn/search?keyword=áo%20thun&category=123&minPrice=50000&maxPrice=100000&sortBy=sales. YouTube/Netflix/Spotify: Lọc video theo thể loại, sắp xếp kết quả tìm kiếm, hay thậm chí là các tham số định dạng video/audio đều có thể dùng query string. API của các ứng dụng di động: Các ứng dụng mobile thường gọi API backend với các tham số phân trang, lọc dữ liệu tương tự như web. 6. Thử nghiệm của Creyt và lời khuyên nên dùng cho case nào Anh Creyt đã từng "đau đầu" với req.query khi xây dựng một hệ thống quản lý tin tức phức tạp. Người dùng có thể lọc bài viết theo hàng chục tiêu chí khác nhau: tác giả, chủ đề, ngày đăng, trạng thái duyệt, độ dài bài viết, v.v. req.query đã giúp anh xử lý linh hoạt mọi yêu cầu đó. Tuy nhiên, nếu không cẩn thận validate, server có thể gặp lỗi hoặc trả về dữ liệu không mong muốn. Khi nào nên "triệu hồi" req.query? GET requests: Đây là "sân chơi" chính của req.query vì nó dùng để yêu cầu dữ liệu từ server và các tham số là để lọc, sắp xếp, phân trang dữ liệu đó. Tham số không nhạy cảm: Bất cứ dữ liệu nào mà việc hiển thị trên URL không gây rủi ro bảo mật (ví dụ: filter category, page number). Tính cacheable (có thể lưu cache): Các request với query string khác nhau có thể được các proxy server hoặc trình duyệt cache riêng biệt, giúp tăng tốc độ tải trang cho các request lặp lại. Khi nào nên "né" req.query? Dữ liệu nhạy cảm: Tuyệt đối không bao giờ truyền mật khẩu, token xác thực, hoặc bất kỳ thông tin cá nhân nhạy cảm nào qua query string. Nó sẽ hiển thị rõ ràng trong lịch sử duyệt web và log server, dễ bị lộ. Thay vào đó, dùng req.body trong POST request hoặc header. Dữ liệu lớn: Nếu bạn cần gửi một lượng lớn dữ liệu (ví dụ: nội dung bài viết dài), URL không phải là nơi lý tưởng. Dùng req.body với method POST/PUT. Tham số bắt buộc và định danh tài nguyên: Nếu tham số đó là một phần của việc định danh tài nguyên (ví dụ: ID của một user), hãy dùng req.params. Ví dụ: /users/123 (ở đây 123 là req.params.id), chứ không phải /users?id=123. Kết luận Vậy đó, GenZ! req.query không chỉ là một khái niệm khô khan mà nó là một "công cụ quyền năng" giúp bạn "thì thầm" những yêu cầu đặc biệt đến server, tạo ra những ứng dụng web linh hoạt và thông minh. Nắm vững nó, bạn sẽ "cân" được rất nhiều kịch bản phức tạp trong phát triển backend. Hãy thực hành thật nhiều để "hack" được trải nghiệm người dùng một cách hiệu quả nhất nhé! Có gì thắc mắc, cứ "bắn" câu hỏi cho anh Creyt! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

42 Đọc tiếp
Bóc tách req.params Node.js: VIP Pass cho API của bạn
23/03/2026

Bóc tách req.params Node.js: VIP Pass cho API của bạn

Chào các đệ tử công nghệ của anh Creyt! Hôm nay, chúng ta sẽ cùng bóc tách một khái niệm mà nghe thì có vẻ "lú", nhưng thực ra nó lại là "key" để API của các bạn trở nên "chanh sả" và linh hoạt hơn rất nhiều: req.params. Hãy tưởng tượng thế này, API của bạn là một cái club đêm cực chất, và mỗi đường link (URL) là một cánh cửa. Để vào được đúng khu vực VIP của một người cụ thể, bạn cần một loại "vé đặc biệt" được in thẳng lên cánh cửa đó. req.params chính là thứ giúp bạn đọc được cái "vé đặc biệt" đó! req.params là gì và để làm gì? req.params trong Node.js (cụ thể là khi dùng framework Express) là một object chứa các thông tin động được trích xuất từ URL của request. Nó giống như khi bạn có một con đường mà trên đó có những "biển số xe" được định nghĩa sẵn trong cấu trúc đường đi vậy. Khi bạn định nghĩa một route trong Express, bạn có thể đặt các placeholder (chỗ giữ chỗ) bằng cách dùng dấu hai chấm (:) trước tên biến. Ví dụ: /users/:id. Cái :id này chính là một parameter (tham số). Khi có một request gửi đến /users/123, Express sẽ tự động bắt lấy 123 và nhét nó vào object req.params với key là id. Vậy là, req.params sẽ trông như thế này: { id: '123' }. Đơn giản là để xác định một tài nguyên (resource) cụ thể mà bạn muốn tương tác. Bạn muốn lấy thông tin của user có ID là 5? Dùng /users/5. Muốn xem một bài post cụ thể? Dùng /posts/hoc-req-params. Nó giúp API của bạn tuân thủ nguyên tắc RESTful, tạo ra các URL sạch sẽ, dễ đọc và dễ quản lý. Code Ví Dụ Minh Họa Để các bạn dễ hình dung, anh Creyt có một ví dụ "nhỏ mà có võ" đây: const express = require('express'); const app = express(); const port = 3000; // Route cơ bản với 1 parameter app.get('/users/:userId', (req, res) => { const userId = req.params.userId; // Trích xuất userId từ URL console.log(`User ID requested: ${userId}`); res.send(`Chào mừng user có ID: ${userId}! Đây là thông tin của bạn.`); }); // Route với nhiều parameter app.get('/products/:category/:productId', (req, res) => { const { category, productId } = req.params; // Dùng destructuring cho gọn console.log(`Category: ${category}, Product ID: ${productId}`); res.send(`Bạn đang xem sản phẩm ${productId} thuộc danh mục ${category}.`); }); // Bắt đầu server app.listen(port, () => { console.log(`Server chạy "phà phà" ở http://localhost:${port}`); console.log(`Thử truy cập:`) console.log(`- http://localhost:${port}/users/genz_dev`); console.log(`- http://localhost:${port}/products/laptop/macbook-pro-m2`); }); Khi chạy đoạn code trên, bạn có thể truy cập: http://localhost:3000/users/genz_dev -> Server sẽ trả về "Chào mừng user có ID: genz_dev! Đây là thông tin của bạn." http://localhost:3000/products/laptop/macbook-pro-m2 -> Server sẽ trả về "Bạn đang xem sản phẩm macbook-pro-m2 thuộc danh mục laptop." Mẹo hay và Best Practices từ anh Creyt Validate là "chân ái": Luôn luôn validate (kiểm tra tính hợp lệ) các giá trị từ req.params. Ví dụ, nếu bạn mong đợi một số nguyên (ID), hãy đảm bảo nó thực sự là số và không phải là một chuỗi lung tung. Ai biết được "đệ tử" nào đó sẽ cố tình gửi /users/hack_me chứ? app.get('/users/:userId', (req, res) => { const userId = parseInt(req.params.userId); // Chuyển đổi sang số nguyên if (isNaN(userId)) { return res.status(400).send('ID user không hợp lệ, "nhức cái đầu" quá!'); } // Xử lý logic với userId hợp lệ res.send(`User ID hợp lệ: ${userId}`); }); Đặt tên "có tâm": Đặt tên parameter rõ ràng, dễ hiểu (ví dụ: userId thay vì id nếu có nhiều loại ID khác nhau). Điều này giúp code của bạn dễ đọc và dễ bảo trì hơn rất nhiều. Phân biệt req.params vs req.query vs req.body: req.params dùng để xác định một tài nguyên cụ thể (ví dụ: /products/123 -> sản phẩm có ID 123). Nó là một phần của URL path và là bắt buộc để định danh tài nguyên. req.query dùng để lọc, sắp xếp, phân trang (ví dụ: /products?category=laptop&sort=price). Nó nằm sau dấu ? trong URL và là tùy chọn. req.body dùng cho dữ liệu gửi kèm trong request body (thường là các request POST, PUT), ví dụ khi gửi form đăng ký hoặc tạo mới một tài nguyên. Tránh xung đột: Đảm bảo các route parameters không xung đột với các route tĩnh. Ví dụ, nếu bạn có /users/new và /users/:userId, hãy đặt route tĩnh (/users/new) lên trước để Express không nhầm lẫn new là một userId. Ứng dụng thực tế và khi nào nên dùng? req.params là "người bạn thân" của bạn trong hầu hết các ứng dụng web và API hiện đại. Bạn sẽ thấy nó xuất hiện "nhan nhản" ở khắp mọi nơi: Website thương mại điện tử (Shopee, Tiki): Khi bạn truy cập /products/ao-thun-nam-sieu-cap-vip-pro-12345 để xem chi tiết một sản phẩm cụ thể. Mạng xã hội (Facebook, Twitter): /profile/creyt_dev hoặc /posts/987654321 để xem trang cá nhân của Creyt hoặc một bài đăng cụ thể. Blog (Medium, Dev.to): /creyt/how-to-master-req-params-in-nodejs để đọc bài viết cụ thể của tác giả Creyt. API của bạn: Bất cứ khi nào bạn muốn lấy, cập nhật, xóa một mục cụ thể trong database (ví dụ: /api/v1/users/5, /api/v1/products/delete/10). Anh Creyt đã từng thử dùng req.query để lấy ID trong một số trường hợp, nhưng sau này mới thấy req.params mới là chuẩn mực RESTful cho việc xác định tài nguyên. Nên dùng req.params khi: Bạn cần một định danh duy nhất (unique identifier) cho một tài nguyên. Cấu trúc URL của bạn cần phản ánh hệ thống phân cấp tài nguyên (ví dụ: /users/:userId/posts/:postId). Bạn muốn URL trông "sạch sẽ", dễ đọc, và trực quan hơn về tài nguyên đang được truy cập. Thử nghiệm đi, các đệ tử! Chạy code ví dụ, thay đổi ID, thay đổi category, và các bạn sẽ thấy sức mạnh của req.params ngay lập tức. Cứ coi nó như là GPS dẫn đường cho API của bạn đến đúng địa chỉ vậy. Nắm vững cái này, là các bạn đã có thêm một skill "xịn sò" để xây dựng các API "đỉnh của chóp" rồi đó! Thuộc Series: Nodejs Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

36 Đọc tiếp