schedule 14 phút đọc

Hono + Bun cho nền tảng AI: 6 pattern sản phẩm đáng học hỏi

Xây dựng nền tảng AI trên Hono + Bun: 100+ services, SSE streaming, sandboxed code execution, real-time WebSocket. 6 pattern sản phẩm từ codebase thực tế.

translate
Có sẵn bằng:
info Bài viết này được dịch bằng AI

Các nền tảng AI chia sẻ một tập hợp chung các yêu cầu về hạ tầng: streaming real-time, workload nặng về file, agent protocols được xác thực bằng schema, auth đa tenant, và nhu cầu di chuyển nhanh mà không hy sinh type safety. Hầu hết các framework server JavaScript được thiết kế trước khi streaming LLM responses và edge deployment là các yêu cầu chuẩn.

Tại QuotyAI chúng tôi xây dựng một nền tảng sales AI deterministic — loại nền tảng tạo ra các hàm pricing TypeScript từ ngôn ngữ tự nhiên, thực thi chúng trong sandbox, và stream kết quả đến agent UIs theo thời gian thực. Backend của chúng tôi chạy 100+ services qua tsyringe, phục vụ các SSE streams, xử lý các WebSocket events qua nhiều kênh messaging, và làm tất cả trong một process Bun.

Hono + Bun là sự kết hợp giúp điều này trở nên khả thi. Hono chỉ nặng dưới 14KB với zero dependencies, cung cấp type inference ở compile-time qua các middleware chains, và chạy trên Bun, Deno, Cloudflare Workers, và Node.js mà không cần thay đổi code. Bun đóng góp native I/O nhanh, các built-in clients cho SQLite và PostgreSQL, và ngày càng nhiều các API cấp hệ điều hành mà trước đây cần các service riêng biệt.

Đây là sáu pattern đã hình thành sau sáu tháng sử dụng trong production.

“Hono buộc bạn phải làm đúng types. Express cho phép bạn ship bugs lên production.”

Lưu ý về phiên bản: Bài viết này đề cập đến Hono ^4.12.27 và Bun latest (tháng 6/2026). Các ví dụ code sử dụng Socket.IO 4.8.3 với @socket.io/bun-engine 0.1.1 — adapter dành cho Bun vẫn còn non trẻ, chúng tôi sẽ đề cập trong Những gì không hiệu quả. Tất cả các ví dụ đều đến từ QuotyAI monorepo.


⚖️ So sánh các Framework cho AI Workloads

Yêu cầu Express FastAPI NestJS Hono
Bundle size ~200KB+ Python runtime overhead ~40KB + deps 14KB
Tính di động của runtime Chỉ Node.js Chỉ Python Node.js Bun, Deno, CF Workers, Node.js
Middleware type safety Không có Pydantic (runtime) Dựa trên decorator Compile-time inference
Hỗ trợ Edge / Worker Cần adapter Không có Cần adapter Native
Streaming (SSE) Triển khai thủ công Async generators Triển khai thủ công streamSSE tích hợp sẵn
Cold start (serverless) 50–100ms 200–500ms 80–150ms 5–10ms

Express coi types là tùy chọn, điều này hoạt động với các CRUD APIs nhưng không phù hợp cho các nền tảng AI nơi mà request validation, business logic, agent execution, và response serialization phải đồng ý trên cùng một types. Chỉ cần một sự không khớp là output bị hỏng.

FastAPI cung cấp đảm bảo type mạnh mẽ qua Pydantic nhưng khóa bạn vào Python — điều này có vấn đề khi bạn cần edge deployment, xử lý ảnh native, hoặc WebSocket multiplexing từ cùng một process.

NestJS thêm cấu trúc nhưng đổi lại là chi phí abstraction overhead. Hệ thống type dựa trên decorator của nó không mở rộng đến các middleware variables hoặc request context xuyên suốt.

Type inference của Hono là duy nhất trong số các JS frameworks: c.get()c.set() được kiểm tra ở compile-time, không phải runtime. Định nghĩa typed Variables trên app và mọi middleware và handler phía sau đều phải khớp, nếu không TypeScript sẽ từ chối build.

💡 Kiến thức độc đáo: Việc thay đổi tenantId từ string thành ObjectId trong định nghĩa type đã phát hiện 14 route handlers cần cập nhật — ở compile-time. Trong Express, những lỗi đó trở thành runtime errors do khách hàng phát hiện. Tính năng duy nhất này đã loại bỏ toàn bộ một loại lỗi khỏi chu kỳ phát triển của chúng tôi.


🏗️ Kiến trúc: Một Process, 30 Route Files

Một process Bun phục vụ cả HTTP và WebSocket thông qua routing dựa trên URL ở transport layer:

import { Server as SocketIOBunEngine } from '@socket.io/bun-engine';
import { Server as SocketIOServer } from 'socket.io';
import { app } from './hono-routes.js';

const io = new SocketIOServer();
const engine = new SocketIOBunEngine();
io.bind(engine);

const realtimeNamespace = io.of('/realtime');
realtimeNamespace.use(async (socket, next) => {
  const token = socket.handshake.auth?.token;
  const user = await authService.verifyToken(token);
  (socket as any).user = user;
  next();
});

export default {
  port: 3000,
  idleTimeout: 30,
  fetch(req, server) {
    const url = new URL(req.url);
    if (url.pathname === '/socket.io/') return engine.handleRequest(req, server);
    return app.fetch(req, server);
  },
  websocket: engine.handler().websocket,
};

Một process. Hono xử lý HTTP. Socket.IO xử lý WebSocket. Không có proxy, không có port thứ hai, không có adapter layer giữa Bun và WebSocket engine.

Hono router mount 30 sub-routers, mỗi router có middleware stack riêng:

const app = new Hono<{ Variables: AppVariables }>();
app.use('*', cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') }));
app.use('*', rateLimitMiddleware({ windowMs: 60_000, max: 100 }));
app.use('*', commonLoggerContextMiddleware);

app.route('/agent-protocol', agentProtocolApp);
app.route('/chat', chatApp);
app.route('/webhooks', webhooksApp);
// ... 27 nhóm route nữa

Các route /agent-protocol có DI middleware và auth checking riêng. Các route /webhooks có xác thực chữ ký HMAC. Không có service locator toàn cầu, không có state chia sẻ giữa các domains.

Sơ đồ kiến trúc cho thấy một process Bun duy nhất phục vụ cả Hono HTTP routes và Socket.IO WebSocket connections trên port 3000, với routing dựa trên URL chuyển tiếp đến handler phù hợp ở transport layer

📦 Pattern 1: Typed Variables loại bỏ Context Bugs

Đây là tính năng làm cho Hono khác biệt hoàn toàn so với Express cho các nền tảng AI đa service.

Định nghĩa shared request context một lần:

export type AppVariables = {
  db: Database;
  container: DependencyContainer;
  user?: AuthUser;
  tenantId?: ObjectId;
};

Mọi c.get()c.set() đều được kiểm tra ở compile-time trên tất cả các middleware và handler. Sub-routers mở rộng type gốc với các dependencies riêng:

type AgentProtocolVariables = {
  agentProtocolService: AgentProtocolService;
  user?: { id: string };
  tenantId: ObjectId;
  authService: AuthService;
};

Mỗi sub-app khai báo chính xác những gì nó cần. Truy cập c.get('somethingUndefined') và TypeScript từ chối nó trước khi test đầu tiên chạy.

Xem Hono middleware docs để biết đầy đủ API reference về typed variables.

💡 Kiến thức độc đáo: Chúng tôi giữ type AppVariables chung trong một file được import bởi mọi route module. Khi một dependency mới cần per-request context, chúng tôi thêm nó ở một nơi và compiler truyền constraint đó qua tất cả 30 nhóm route. Không có runtime discovery. Không có bất ngờ undefined lúc 3 giờ sáng.


🔄 Pattern 2: SSE Streaming cho Agent Communication

Các AI agents giao tiếp theo streams. Output token LLM từng token một, chuỗi tool-call, reasoning dài — tất cả đều map tự nhiên sang Server-Sent Events. Helper streamSSE của Hono xử lý vòng đời với filtered event sinks và quản lý heartbeat:

import { streamSSE } from 'hono/streaming';

app.post('/threads/:threadId/stream/events', async (c) => {
  const params = await c.req.json();
  const service = c.get('agentProtocolService');

  return streamSSE(c, async (stream) => {
    const sinkId = crypto.randomUUID();
    let detached = false;

    const detachSink = () => {
      if (detached) return;
      detached = true;
      service.detachEventSink(threadId, sinkId);
    };

    const send = async (event: any) => {
      if (detached || stream.aborted) return;
      try {
        await stream.writeSSE({
          id: event.event_id,
          event: event.method,
          data: JSON.stringify(event),
        });
      } catch {
        detachSink();
      }
    };

    await service.attachFilteredEventSink(threadId, {
      id: sinkId,
      filter: {
        channels: new Set(params.channels ?? ['messages', 'tools', 'lifecycle']),
        namespaces: params.namespaces,
      },
      send,
    });

    stream.onAbort(detachSink);

    const heartbeatTimer = setInterval(async () => {
      if (stream.aborted) return clearInterval(heartbeatTimer);
      await stream.writeSSE({ event: 'ping', data: '{"type":"heartbeat"}' });
    }, 30_000);

    await new Promise<void>((resolve) => {
      stream.onAbort(() => { detachSink(); resolve(); });
    });
  });
});

Heartbeat 30 giây giữ kết nối sống qua các proxies và load balancers. Pattern detachSink ngăn chặn memory leaks khi clients ngắt kết nối mà không có đóng kết nối sạch — điều này xảy ra thường xuyên trên mạng di động.

HTTP server của Bun xử lý streaming bodies mà không cần cấu hình. Không có adapter, không có middleware đặc biệt.

💡 Kiến thức độc đáo: Guard pattern detachSink được thêm vào sau một sự cố production khi một client trên di động điều hướng đi giữa chừng stream. Nếu không có nó, mỗi kết nối bị hủy sẽ leak một event sink subscription. Callback stream.onAbort hoạt động đáng tin cậy trên Bun, nhưng khoảng trống async giữa các kiểm tra stream.aborted và các lệnh gọi writeSSE có nghĩa là bạn cần cả cờ callback để bảo vệ hoàn toàn.

Sơ đồ trình tự cho thấy vòng đời SSE streaming: client kết nối, Hono streamSSE tạo một filtered event sink với heartbeat timer, agent events chảy qua sink đến writeSSE, và khi client ngắt kết nối, pattern detachSink dọn dẹp subscription và interval

Tham khảo Hono streaming docs để biết API reference đầy đủ về streamSSE.


🏖️ Pattern 3: Sandboxed Code Execution cho các hàm do AI tạo ra

Các nền tảng AI tạo và thực thi code cần sandboxing ở runtime level. Module vm của Node.js cung cấp điều này với danh sách cho phép nghiêm ngặt và timeout cứng:

import * as vm from 'vm';

const SANDBOX_TIMEOUT_MS = 5000;

function executeCode<T>(code: string, input: unknown, functionName: string): T {
  const sandbox = {
    console: { log: (...args) => logger.debug(...args) },
    Date, Math, Array, Object, String, Number, Boolean, RegExp, JSON,
    Error, TypeError, ReferenceError, RangeError,
    parseInt, parseFloat, isNaN, isFinite,
  };

  const context = vm.createContext(sandbox, {
    name: 'Sandbox',
    codeGeneration: { strings: false, wasm: false },
  });

  const script = new vm.Script(
    `${code}\n__result__ = ${functionName};`,
    { filename: `sandbox-${functionName}.js` }
  );

  script.runInContext(context, { timeout: SANDBOX_TIMEOUT_MS });

  const runner = (sandbox as any).__result__;
  return Array.isArray(input) ? runner(...input) : runner(input);
}

Các ràng buộc chính:

  • Chỉ danh sách cho phép: Date, Math, Array, Object, JSON, các error constructors cơ bản, parseInt, parseFloat. Không có require, không có import, không có eval, không có fetch, không có process.
  • Không có dynamic code generation: codeGeneration: { strings: false, wasm: false } chặn các pattern kiểu eval bên trong sandbox.
  • Timeout cứng: 5 giây giết các infinite loops. Code do AI tạo ra tạo ra chúng thường xuyên hơn bạn nghĩ.

Khi thực thi thất bại, lưu script để debug:

async function saveFailedScript(code: string, error: Error) {
  const filename = `failed-script-${Date.now()}.js`;
  await fs.mkdir('./troubleshooting-scripts', { recursive: true });
  await fs.writeFile(
    `./troubleshooting-scripts/${filename}`,
    `// Error: ${error.message}\n\n${code}`
  );
}

💡 Kiến thức độc đáo: Ba edge cases production đã được phát hiện theo cách này: infinite loops từ đệ quy malformed trong code được tạo ra, lan truyền NaN từ việc thiếu kiểm tra null, và một timeout từ một hàm được tạo ra sử dụng while(true). Nếu không có các script đã lưu, mỗi trường hợp sẽ là một phiên debug nhiều giờ. Thư mục troubleshooting bây giờ là nơi đầu tiên chúng tôi kiểm tra khi sandbox execution thất bại.

Sơ đồ pipeline cho thấy: đầu vào người dùng → AI tạo các hàm TypeScript → code đi vào sandbox vm.createContext với danh sách cho phép nghiêm ngặt (không require, không fetch, không process) → timeout 5 giây → kết quả hoặc lỗi với troubleshooting script được lưu ra đĩa

⚡ Pattern 4: Database Change Streams đến WebSocket

Các event pipelines real-time cho nền tảng AI có thể được xây dựng mà không cần message broker bằng cách nối database change streams đến WebSocket:

MongoDB ChangeStream (6 collections)
  → eventBus.emit() (typed EventEmitter, 27 event types)
    → Socket.IO adapter (lắng nghe tất cả 27 events)
      → Room emissions (tenant:*, conversation:*)

Khi một tin nhắn mới đến qua bất kỳ kênh nào (Telegram, Facebook, web form), Change Stream phát hiện document, event bus emit một typed event, và Socket.IO adapter phát sóng đến subscribers:

export function setupSocketIOAdapters(io: SocketIOServer) {
  eventBus.on('conversation:message:created', (event) => {
    io.to(`conversation:${event.conversationId}`)
      .emit('conversation:message:created', event);
  });

  eventBus.on('agent:status:changed', (event) => {
    io.to(`tenant:${event.tenantId}`)
      .emit('agent:status:changed', event);
  });
  // ... 25 event types nữa
}

Không có message broker, không có Redis pub/sub. Một process xử lý toàn bộ pipeline. Khi cần horizontal scaling, @socket.io/redis-adapter thay vào mà không cần thay đổi event mapping layer — adapter nằm giữa Socket.IO và Redis, không phải giữa code của bạn và Socket.IO.

Xem MongoDB Change Streams docs để biết các yêu cầu thiết lập (replica set, oplog access).


🛡️ Pattern 5: RFC 9457 Problem Details cho Structured Errors

Các error responses chuẩn hóa giúp tiết kiệm hàng giờ debug. Package hono-problem-details map các framework errors sang định dạng RFC 9457:

app.onError(
  problemDetailsHandler({
    autoInstance: true,
    mapError: (error) => {
      if (error instanceof HTTPException && error.cause instanceof ZodError) {
        return {
          status: 422,
          title: 'Validation Error',
          detail: 'Request validation failed',
          extensions: {
            errors: error.cause.issues.map((issue) => ({
              field: issue.path.join('.'),
              message: issue.message,
              code: issue.code,
            })),
          },
        };
      }

      if (error instanceof LlmConfigError) {
        return { status: 404, title: 'LLM Configuration Not Found', detail: error.message };
      }

      if (error instanceof TenantError) {
        return { status: 409, title: error.message, detail: error.detail };
      }
    },
    localize: (pd, c) => {
      const correlationId = c.req.header('x-correlation-id');
      return { ...pd, correlationId, tenantId: c.get('tenantId')?.toString() };
    },
  }),
);

Mỗi error response bao gồm một correlation ID, tenant context, và structured validation details. Khi một client báo cáo “API trả về lỗi,” bạn trace được đến chính xác request, tenant, và validation failure.

💡 Kiến thức độc đáo: Callback localize trong problemDetailsHandler là hook giải quyết vấn đề “tenant nào bị lỗi?” của chúng tôi. Trước khi thêm correlation IDs vào mọi error response, debug một sự cố multi-tenant có nghĩa là grep logs theo khoảng thời gian. Bây giờ mọi error đều mang theo tenant context một cách tự nhiên — bao gồm cả validation errors xảy ra trước khi middleware tenant chạy (sử dụng header x-tenant-id trực tiếp từ request).

Xem hono-problem-details docs để biết đầy đủ API.


🧩 Pattern 6: Dependency Injection theo Dependency Level

Khi bạn đăng ký 100+ services, việc sắp xếp thủ công trở nên dễ lỗi. Tổ chức registrations theo dependency level ngăn chặn circular resolution và làm cho dependency graph trở nên rõ ràng:

export function registerServices(container: DependencyContainer) {
  // Level 0 — không có dependencies
  container.registerSingleton(EncryptionService);
  container.registerSingleton(DynamicRunnerService);

  // Level 1 — phụ thuộc vào Level 0
  container.registerSingleton(TenantStorage);
  container.registerSingleton(ApiKeyStorage);

  // Level 2 — phụ thuộc vào Levels 0–1
  container.registerSingleton(TenantService);
  container.registerSingleton(ApiKeyService);

  // ... đến Level 7
}

Mỗi route file resolve các services của riêng nó thông qua middleware, không phải global locator:

const agentProtocolMiddleware = async (c: Context, next: Next) => {
  const service = container.resolve(AgentProtocolService);
  const authService = container.resolve(AuthService);
  c.set('agentProtocolService', service);
  c.set('authService', authService);
  await next();
};

Không có import-time side effects. Mỗi middleware khai báo dependencies một cách tường minh. DI container resolve chúng một lần khi khởi động.

💡 Kiến thức độc đáo: Chúng tôi sử dụng tsyringe với 8 dependency levels. Level 0 là các utilities thuần túy (encryption, sandbox runner). Level 7 là các concerns xuyên suốt (analytics, observability). Comment về level tại mỗi registration làm cho circular dependencies lập tức hiển thị khi code review — bạn có thể thấy ngay liệu một service Level 2 có vô tình import dependency Level 5 hay không.


⚠️ Những gì không hiệu quả

Socket.IO trên Bun cần @socket.io/bun-engine. Socket.IO server tiêu chuẩn giả định Node.js http.Server. Adapter dành cho Bun hiện ở phiên bản 0.1.1 — nó hoạt động, nhưng sẽ có một số điểm chưa ổn định về timeouts và xử lý kết nối. Hãy kiểm tra kỹ logic reconnect của bạn.

Một số packages npm giả định Node.js internals. Hai vấn đề trong production: một package sử dụng process.nextTick theo cách Bun xử lý khác, một cái khác mong đợi hành vi fs.watch mà Bun không tái tạo đầy đủ. Cả hai đều có thể sửa trong một ngày, nhưng nên kiểm tra dependency tree trước khi cam kết với Bun.

Type inference của Hono nghiêm ngặt theo thiết kế. Thay đổi types của c.set() trong middleware và mọi handler phía sau đều phải khớp. Đây là tính năng ngăn chặn runtime bugs, nhưng nó có nghĩa là refactor middleware cần sửa nhiều file hơn so với Express. Trade-off xứng đáng cho sự chính xác.

Không có xử lý ảnh sẵn sàng cho production. Bun.Image tồn tại nhưng với các workload nghiêm trọng bạn vẫn sẽ muốn sharp hoặc một service chuyên dụng. Cách tiếp cận dựa trên buffer hoạt động cho sử dụng nhẹ.


📊 Số liệu hiệu suất

Đo trong production, không phải benchmarks:

Chỉ số Giá trị
Sandboxed function execution 24ms
API response (với LLM) 1–2s
Tải một document (MongoDB) 50ms
SSE heartbeat interval 30s
Sandbox timeout 5.000ms
Rate limit mặc định 100 req/60s

Thời gian sandbox execution 24ms quan trọng vì nó tách biệt business logic deterministic khỏi độ trễ LLM. Người dùng nhận được kết quả nhanh, nhất quán cho bất kỳ thứ gì không cần gọi AI. Để hiểu sâu hơn về chi phí deployment đằng sau các con số này, xem Tôi chạy một AI Startup Production với $30/Tháng như thế nào.


📋 Stack

Layer Công nghệ Phiên bản
Runtime Bun latest
HTTP framework Hono ^4.12.27
WebSocket Socket.IO + @socket.io/bun-engine ^4.8.3 / ^0.1.1
DI container tsyringe ^4.10.0
Validation Zod 4.3.6
Database MongoDB native driver ^7.3.0
AI orchestration LangChain + LangGraph ^1.2.1 / ^1.4.6
Logging LogTape + OpenTelemetry ^2.1.5

📋 Khi nào nên sử dụng Stack này

Sử dụng Hono + Bun khi:

  • Bạn cần compile-time type safety xuyên suốt middleware chains
  • Bạn muốn một process cho HTTP, WebSocket, và background work
  • Bạn đang deploy lên cả edge và traditional servers từ cùng một codebase
  • Team bạn viết TypeScript và muốn compiler enforcement tối đa

Không nên sử dụng khi:

  • Bạn phụ thuộc vào các packages npm giả định Node.js internals (kiểm tra dependencies trước)
  • Bạn cần horizontal scaling ngay từ đầu (lên kế hoạch tích hợp @socket.io/redis-adapter sớm)
  • Team bạn cần tài liệu đã được kiểm chứng rộng rãi (tài liệu Hono tốt nhưng chưa đạt mức Express)

Để biết quy trình ra quyết định ban đầu đằng sau việc chọn các công cụ này, xem Tại sao tôi chọn Bun và Hono.


🗺️ Tiếp theo là gì

Bốn cải tiến trên roadmap:

  • Per-tenant rate limiting. Các nền tảng AI phải ngăn chặn lạm dụng prompt-cost. Model middleware của Hono làm cho điều này có thể kết hợp — key theo limiter bằng tenant ID, không chỉ IP.
  • Streaming timeout handling. Khi một upstream LLM bị dừng giữa stream, SSE connection bị treo. Handler streamSSE cần logic timeout nhận biết backpressure.
  • Background workers. Chuyển embedding generation và batch evaluation sang các Bun.Worker threads giữ cho vòng lặp request chính responsive.
  • Cost tracking middleware. Accounting cấp token cho mỗi request, hiển thị qua response hooks, cho tenants thấy rõ chi phí AI agents của họ.

Mỗi tính năng đều nằm trong cùng codebase Hono + Bun. Không có migration framework. Không có rewrite.

Đó là ý nghĩa.


TL;DR

Danh mục Pattern
Lựa chọn framework Hono thay vì Express, FastAPI, NestJS cho AI workloads
Kiến trúc server Một process Bun, Hono HTTP + Socket.IO WebSocket trên một port
Type safety Typed AppVariables — enforcement ở compile-time trên tất cả middleware
Agent streaming streamSSE với filtered event sinks, heartbeat, và detach-on-abort
Code sandboxing vm.createContext() với danh sách cho phép nghiêm ngặt, codeGeneration: false, timeout 5s
Real-time events MongoDB ChangeStream → typed EventBus → Socket.IO rooms
Xử lý lỗi RFC 9457 Problem Details với correlation IDs và tenant context
DI tsyringe với dependency levels để ngăn chặn circular resolution
Streaming SSE cho agent events một chiều, WebSocket cho UI interactions hai chiều

Đọc thêm


Câu hỏi thường gặp

Tại sao chọn Hono thay vì Express cho nền tảng AI? Hono chỉ nặng dưới 14KB, không có dependency, cung cấp type inference cho middleware variables ở compile-time, và chạy trên Bun, Deno, Cloudflare Workers, và Node.js mà không cần thay đổi code. Express coi type safety là thứ thêm vào sau và cần adapters cho edge runtimes.

Bun đã sẵn sàng cho production với backend AI chưa? Bun xử lý được các workload production bao gồm HTTP serving, WebSocket upgrades, MongoDB connections, và sandboxed code execution. Điểm yếu chính là sự trưởng thành của hệ sinh thái npm — một số packages giả định các internals riêng của Node.js. Socket.IO engine dành cho Bun hiện ở phiên bản 0.1.1, nên sẽ có một số điểm chưa ổn định.

Làm thế nào để chạy code do AI tạo ra một cách an toàn trong production? Sử dụng Node.js vm.createContext() với danh sách cho phép nghiêm ngặt gồm các globals (Date, Math, Array, Object, JSON), vô hiệu hóa code generation, và timeout 5 giây. Sandbox không có quyền truy cập filesystem, network, hay process. Các script lỗi được lưu ra đĩa kèm context lỗi để debug.

Sự khác biệt giữa SSE và WebSocket cho giao tiếp agent là gì? SSE là streaming một chiều từ server đến client — lý tưởng cho output token LLM và event streams của agent. WebSocket là hai chiều — cần thiết cho typing indicators, cập nhật UI real-time, và điều phối multi-agent khi client cần gửi events ngược lại. Hono hỗ trợ cả hai: streamSSE cho SSE, và Socket.IO trên cùng một process Bun cho WebSocket.

Làm thế nào để xử lý multi-tenant context trong Hono middleware? Định nghĩa typed AppVariables trên Hono app với tenantId: ObjectId. Auth middleware trích xuất tenant từ JWT hoặc header và c.set('tenantId', ...). Mọi handler phía sau truy cập nó qua c.get('tenantId') — được kiểm tra ở compile-time, không có runtime lookups.


Tags: hono bun typescript ai-platform backend-architecture

Bài viết hữu ích? Hãy chia sẻ.

Bài viết liên quan

Cảm ơn bạn đã đọc!
Đọc thêm bài viết