npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

elysia-nnn-socket

v0.0.2

Published

A WebSocket plugin for Elysia framework - file-based socket routes

Readme

elysia-nnn-socket

npm version npm downloads license

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

  • 🚀 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-socket

Cách dùng cơ bản

  1. Tạo thư mục sockets trong dự án.
  2. 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
  1. 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.tsWS /api/ws/echo
  • sockets/room/[id]/index.tsWS /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

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ặc drain)
  • Kiểm tra path: Xác minh path khớp với cấu trúc file (dùng option prefix nế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 dir trỏ đúng thư mục (relative to process.cwd())
  • Phần mở rộng file: Kiểm tra file socket có phần mở rộng .ts hoặ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.

  1. Fork repository
  2. Tạo feature branch (git checkout -b feature/amazing-feature)
  3. Commit changes (git commit -m 'Add some amazing feature')
  4. Push lên branch (git push origin feature/amazing-feature)
  5. 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

Liên kết