Mở đầu: Sức mạnh của ứng dụng Realtime
Trong thời đại số hiện nay, người dùng không còn chấp nhận việc phải refresh trang để xem nội dung mới. Họ kỳ vọng mọi thay đổi xuất hiện ngay lập tức — dù đó là tin nhắn chat, cập nhật trạng thái, hay bảng điểm trực tiếp. Đây chính là lúc công nghệ Realtime tỏa sáng, giúp ứng dụng trở nên sống động và tương tác hơn bao giờ hết.
Để xây dựng tính năng này, các nhà phát triển thường phải đối mặt với thách thức về hạ tầng, bảo mật, và đồng bộ dữ liệu. May mắn thay, có những công cụ hiện đại giúp đơn giản hóa mọi thứ. Trong bài viết này, chúng ta sẽ khám phá cách kết hợp Supabase — một nền tảng Backend-as-a-Service (BaaS) mã nguồn mở — với Socket.io — thư viện hỗ trợ giao tiếp thời gian thực — để tạo ra một ứng dụng Realtime mạnh mẽ và dễ bảo trì.
Supabase và Socket.io: Tại sao nên kết hợp?
Supabase là gì?
Supabase cung cấp một bộ công cụ đầy đủ: cơ sở dữ liệu PostgreSQL, xác thực người dùng, lưu trữ tệp, và đặc biệt là thay đổi thời gian thực (Realtime) dựa trên WebSocket. Với vài dòng code, bạn có thể lắng nghe mọi thay đổi trong cơ sở dữ liệu và cập nhật giao diện ngay lập tức.
Socket.io là gì?
Socket.io là thư viện phổ biến cho phép giao tiếp hai chiều giữa client và server theo thời gian thực. Nó hỗ trợ nhiều kênh (rooms/channels), quản lý kết nối, và đảm bảo tin nhắn được gửi đi ngay cả khi mạng không ổn định.
Lợi ích khi kết hợp
– Tách biệt nhiệm vụ: Supabase quản lý dữ liệu và xác thực; Socket.io xử lý giao tiếp giữa người dùng.
– Mở rộng dễ dàng: Mỗi lớp có thể scale độc lập.
– Bảo mật: Supabase đảm bảo quyền truy cập dữ liệu; Socket.io kiểm soát kênh giao tiếp.
– Nhanh chóng: Tận dụng sức mạnh của cả hai mà không phải xây dựng từ đầu.
Kiến trúc ứng dụng Realtime
Để hiểu rõ hơn, hãy hình dung luồng hoạt động:
1. Client gửi yêu cầu thay đổi dữ liệu lên Supabase.
2. Supabase cập nhật cơ sở dữ liệu và phát tín hiệu Realtime.
3. Server Socket.io lắng nghe tín hiệu từ Supabase, xử lý logic (như gửi thông báo cho room cụ thể).
4. Client kết nối đến Socket.io nhận cập nhật và hiển thị ngay lập tức.
Với kiến trúc này, bạn có thể xây dựng các tính năng như chat nhóm, bảng điểm trực tiếp, hay hệ thống thông báo mà không cần lo lắng về độ phức tạp của hạ tầng.
Triển khai thực tế: Chat Realtime
Bước 1: Thiết lập Supabase
Đầu tiên, tạo một project mới trên Supabase. Trong cơ sở dữ liệu, tạo bảng messages với các cột:
– id (UUID, primary key)
– room_id (text)
– user_id (text)
– content (text)
– created_at (timestamp, default now())
Kích hoạt Realtime cho bảng này trong phần Settings > Database > Replication, chỉ cho phép INSERT và UPDATE.
Bước 2: Xây dựng Server Socket.io
// server.js
const { createServer } = require('http');
const { Server } = require('socket.io');
const { v4: uuidv4 } = require('uuid');
const { createClient } = require('@supabase/supabase-js');
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
const server = createServer();
const io = new Server(server, {
cors: { origin: "*" }
});
// Lắng nghe kết nối từ client
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
// Join room
socket.on('join-room', (roomId) => {
socket.join(roomId);
console.log(User ${socket.id} joined room ${roomId});
});
// Gửi message
socket.on('send-message', async ({ roomId, userId, content }) => {
const { data, error } = await supabase
.from('messages')
.insert({ room_id: roomId, user_id: userId, content });
if (error) {
socket.emit('message-error', error.message);
} else {
io.to(roomId).emit('new-message', data[0]);
}
});
// Rời room
socket.on('leave-room', (roomId) => {
socket.leave(roomId);
});
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
});
});
server.listen(3001, () => {
console.log('Socket.io server listening on port 3001');
});
Bước 3: Client kết nối cả hai dịch vụ
// client.js
import { createClient } from '@supabase/supabase-js';
import { io } from 'socket.io-client';
const supabaseUrl = 'https://<your-project>.supabase.co';
const supabaseKey = '<your-anon-key>';
const supabase = createClient(supabaseUrl, supabaseKey);
const socket = io('http://localhost:3001');
// Join room
const roomId = 'room-1';
socket.emit('join-room', roomId);
// Lắng nghe tin nhắn mới
socket.on('new-message', (message) => {
console.log('New message:', message);
// Cập nhật giao diện
});
// Gửi tin nhắn
async function sendMessage(content) {
const userId = supabase.auth.user().id;
socket.emit('send-message', { roomId, userId, content });
}