elysia-nnn-socket
v0.0.2
Published
A WebSocket plugin for Elysia framework - file-based socket routes
Maintainers
Readme
elysia-nnn-socket
English | Tiếng Việt
Phiên bản hiện tại: 0.0.2
Plugin WebSocket cho Elysia framework, tự động quét và đăng ký các route WebSocket từ cấu trúc thư mục, tương tự elysia-nnn-router.
Mục lục
- Tính năng
- Cài đặt
- Cách dùng cơ bản
- Cấu hình
- Quy ước đường dẫn
- Viết Socket Handler
- Schema Validation
- Middleware
- Xử lý lỗi
- Tùy chọn nâng cao
- Hỗ trợ TypeScript
- Ví dụ thực tế
- Khắc phục sự cố
- FAQ
- Hiệu suất
- Đóng góp
- Yêu cầu
- License
Tính năng
- 🚀 Tự động quét và đăng ký WebSocket routes từ thư mục
- 🔌 Hỗ trợ handler
open,message,close,drain - 🎯 Đường dẫn động với cú pháp
[param](vd./room/:id) - 🛡️ Middleware theo thư mục qua
_middleware.ts - ⚡ Dành cho Bun và Elysia
- 📦 Hỗ trợ TypeScript
Cài đặt
bun add elysia-nnn-socketCách dùng cơ bản
- Tạo thư mục
socketstrong dự án. - Thêm các file xử lý WebSocket (mỗi file = một endpoint):
sockets/
├── _middleware.ts # Tùy chọn: beforeHandle cho mọi socket
├── echo/
│ └── index.ts # WS /echo
├── chat/
│ └── index.ts # WS /chat
└── room/
└── [id]/
└── index.ts # WS /room/:id- Dùng plugin trong app:
import { Elysia } from "elysia";
import { nnnSocketPlugin } from "elysia-nnn-socket";
const app = new Elysia();
app.use(nnnSocketPlugin());
app.listen(3000, () => {
console.log("🚀 Server tại http://localhost:3000");
});Cấu hình
app.use(
nnnSocketPlugin({
dir: "sockets", // Thư mục quét (mặc định: "sockets")
prefix: "/api/ws", // Prefix cho mọi socket (mặc định: "")
silent: false, // Tắt logging (mặc định: false)
onError: (error, filePath) => {
// Xử lý lỗi tùy chỉnh
console.error(`Lỗi trong ${filePath}:`, error);
},
})
);Với prefix: "/api/ws":
sockets/echo/index.ts→WS /api/ws/echosockets/room/[id]/index.ts→WS /api/ws/room/:id
Quy ước đường dẫn
- Path từ file: Đường dẫn tương đối từ
dir, bỏ phần mở rộng.[param]thành:param. - File index:
sockets/chat/index.ts→ path/chat. - File có tên:
sockets/custom/live.ts→ path/custom/live.
Viết Socket Handler
Export open, message, close và/hoặc drain. Cần ít nhất một handler.
// sockets/echo/index.ts
export default {
open(ws) {
console.log("Client đã kết nối");
},
message(ws, message) {
ws.send(message);
},
close(ws) {
console.log("Client ngắt kết nối");
},
};Hoặc export named:
// sockets/chat/index.ts
export function open(ws) {
ws.send(JSON.stringify({ type: "connected" }));
}
export function message(ws, message) {
ws.send(message);
}Tham số động
// sockets/room/[id]/index.ts
export default {
open(ws) {
const roomId = ws.data.params?.id;
ws.send(JSON.stringify({ roomId }));
},
message(ws, message) {
ws.send(message);
},
};Schema Validation
Bạn có thể validate message WebSocket bằng schema của Elysia:
// sockets/chat/index.ts
import { t } from "elysia";
export default {
body: t.Object({
message: t.String(),
userId: t.String(),
}),
message(ws, { message, userId }) {
ws.send(
JSON.stringify({
message,
userId,
timestamp: Date.now(),
})
);
},
};Middleware
Đặt _middleware.ts trong bất kỳ thư mục nào. Handler chạy trước khi nâng cấp WebSocket (giống Elysia beforeHandle).
// sockets/_middleware.ts
export default ({ headers, error }) => {
const token = headers.authorization;
if (!token) {
return error(401, { message: "Unauthorized" });
}
};Xử lý lỗi
Xử lý lỗi trong socket handlers:
// sockets/secure/index.ts
export default {
beforeHandle({ headers, error }) {
const token = headers.authorization;
if (!token) {
return error(401, { message: "Unauthorized" });
}
},
message(ws, message) {
try {
// Logic của bạn
const data = JSON.parse(message);
ws.send(JSON.stringify({ success: true, data }));
} catch (err) {
ws.send(
JSON.stringify({
error: err instanceof Error ? err.message : "Lỗi không xác định",
})
);
}
},
};Tùy chọn nâng cao
Transform Message
Transform message trước khi validate:
// sockets/transform/index.ts
export default {
transformMessage: (message) => {
// Parse JSON string thành object
return JSON.parse(message.toString());
},
body: t.Object({
type: t.String(),
data: t.Any(),
}),
message(ws, { type, data }) {
// data đã là object được parse
ws.send(JSON.stringify({ received: type, data }));
},
};Custom Headers
Thêm custom headers trước khi upgrade WebSocket:
// sockets/custom/index.ts
export default {
header: {
"X-Custom-Header": "value",
},
message(ws, message) {
ws.send(message);
},
};Yêu cầu
- Bun v1.2+
- Elysia ^1.3+
License
MIT
Tác giả
The Anh
- GitHub: @theanh-it
- Email: [email protected]
Hỗ trợ TypeScript
Package export types để hỗ trợ TypeScript tốt hơn:
import type {
SocketModule,
SocketHandler,
WebSocketContext,
} from "elysia-nnn-socket";
// Sử dụng trong socket files
const handler: SocketHandler = {
open(ws: WebSocketContext) {
console.log("Đã kết nối:", ws.data.params);
},
message(ws: WebSocketContext, message: string | Buffer) {
ws.send(message);
},
};Ví dụ thực tế
Ứng dụng Chat
// sockets/chat/index.ts
import { t } from "elysia";
export default {
body: t.Object({
userId: t.String(),
message: t.String(),
}),
open(ws) {
ws.subscribe("chat");
ws.publish(
"chat",
JSON.stringify({
type: "user_joined",
userId: ws.data.query?.userId,
})
);
},
message(ws, { userId, message }) {
// Gửi đến tất cả clients đã kết nối
ws.publish(
"chat",
JSON.stringify({
userId,
message,
timestamp: Date.now(),
})
);
},
};Thông báo real-time
// sockets/notifications/[userId]/index.ts
export default {
open(ws) {
const userId = ws.data.params?.userId;
// Subscribe user vào channel thông báo của họ
ws.subscribe(`notifications:${userId}`);
},
message(ws, message) {
// Xử lý message thông báo
const userId = ws.data.params?.userId;
ws.publish(`notifications:${userId}`, message);
},
};Khắc phục sự cố
Kết nối WebSocket thất bại
- Kiểm tra exports: Đảm bảo file socket export ít nhất một handler (
open,message,close, hoặcdrain) - Kiểm tra path: Xác minh path khớp với cấu trúc file (dùng option
prefixnếu cần) - Console trình duyệt: Kiểm tra console trình duyệt để xem lỗi kết nối
- Server logs: Bật logging bằng cách bỏ
silent: trueđể xem các route đã đăng ký
Lỗi module not found
- Đường dẫn thư mục: Đảm bảo option
dirtrỏ đúng thư mục (relative toprocess.cwd()) - Phần mở rộng file: Kiểm tra file socket có phần mở rộng
.tshoặc.js - Format export: Xác minh file export đúng (default export hoặc named exports)
Lỗi type
- TypeScript: Đảm bảo đã cài TypeScript (
bun add -d typescript) - Import types: Import types rõ ràng:
import type { SocketHandler } from "elysia-nnn-socket" - Type definitions: Đảm bảo
dist/index.d.tsđược tạo sau khi build
FAQ
Q: Tôi có thể dùng cả HTTP routes và WebSocket routes không?
A: Có! Bạn có thể dùng elysia-nnn-router cho HTTP routes và elysia-nnn-socket cho WebSocket routes trong cùng một Elysia app.
Q: Làm sao xử lý kết nối lại?
A: Handler open được gọi mỗi khi client kết nối. Bạn có thể dùng nó để khôi phục state, re-subscribe channels, hoặc gửi dữ liệu ban đầu.
Q: Tôi có thể dùng middleware từ elysia-nnn-router không?
A: Có, cấu trúc middleware tương tự. Tuy nhiên, middleware WebSocket chạy trước khi upgrade (trong beforeHandle), không phải trên mỗi message.
Q: Làm sao broadcast đến tất cả clients?
A: Dùng ws.publish(channel, message) để broadcast đến tất cả clients đã subscribe channel. Dùng ws.subscribe(channel) trong handler open.
Q: Tôi có thể dùng async handlers không?
A: Có! Tất cả handlers (open, message, close, drain) có thể là async functions trả về Promise<void>.
Hiệu suất
- Startup: Routes được quét một lần khi khởi động (không có overhead runtime)
- WebSocket: Sử dụng Bun's native WebSocket implementation (hiệu suất cao)
- Middleware: Được cache theo thư mục để tránh truy cập file system lặp lại
- File scanning: Sử dụng Bun's Glob API để tối ưu hiệu suất
- Memory: Footprint bộ nhớ tối thiểu, chỉ lưu định nghĩa routes
Đóng góp
Mọi đóng góp đều được chào đón! Vui lòng tạo Pull Request.
- Fork repository
- Tạo feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add some amazing feature') - Push lên branch (
git push origin feature/amazing-feature) - Mở Pull Request
Vui lòng đảm bảo code của bạn:
- Tuân theo code style hiện tại
- Có tests cho features mới
- Cập nhật documentation khi cần
