New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

verani

Package Overview
Dependencies
Maintainers
1
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

verani

A simple, focused realtime SDK for Cloudflare Actors with Socket.io-like semantics

latest
npmnpm
Version
0.13.3
Version published
Maintainers
1
Created
Source

Verani

MADE BY #V0ID

Build realtime apps on Cloudflare with Socket.io-like simplicity

Getting StartedDocumentationExamples

Verani brings the familiar developer experience of Socket.io to Cloudflare's Durable Objects (Actors), with proper hibernation support and minimal overhead. Build realtime chat, presence systems, notifications, and more—all running on Cloudflare's edge.

Why Verani?

  • Familiar API: If you've used Socket.io, you already know how to use Verani
  • Horizontally scalable: Per-connection architecture - each user gets their own Durable Object
  • Hibernation support: Handles Cloudflare Actor hibernation automatically
  • Type safe: Built with TypeScript, full type safety throughout
  • Simple mental model: Connections, rooms, and broadcast semantics that just make sense
  • Modern DX: Automatic reconnection, error handling, and connection lifecycle management
  • Edge-ready: Built for Cloudflare Workers and Durable Objects
  • Cost-efficient: Idle connections hibernate and cost nothing

Quick Start

Get a realtime chat app running in 5 minutes.

Step 1: Install

npm install verani @cloudflare/actors

Don't have a Cloudflare Worker project? Create one:

npm create cloudflare@latest my-verani-app
cd my-verani-app
npm install verani @cloudflare/actors

Step 2: Create Your Connection Handler

Create src/actors/connection.ts:

import { defineConnection, createConnectionHandler, createRoomHandler } from "verani";

// Define connection handler (one WebSocket per user)
const userConnection = defineConnection({
  name: "UserConnection",

  extractMeta(req) {
    const url = new URL(req.url);
    const userId = url.searchParams.get("userId") || crypto.randomUUID();
    return {
      userId,
      clientId: crypto.randomUUID(),
      channels: ["default"]
    };
  },

  async onConnect(ctx) {
    console.log(`User ${ctx.meta.userId} connected`);
    // Join chat room (persisted across hibernation)
    await ctx.actor.joinRoom("chat");
  },

  async onDisconnect(ctx) {
    console.log(`User ${ctx.meta.userId} disconnected`);
    // Room leave is handled automatically
  }
});

// Handle messages (socket.io-like API)
userConnection.on("chat.message", async (ctx, data) => {
  // Broadcast to everyone in the chat room
  await ctx.emit.toRoom("chat").emit("chat.message", {
    from: ctx.meta.userId,
    text: data.text,
    timestamp: Date.now()
  });
});

// Export the connection handler
export const UserConnection = createConnectionHandler(userConnection);

// Export the room coordinator
export const ChatRoom = createRoomHandler({ name: "ChatRoom" });

Step 3: Wire Up Your Worker

Update src/index.ts:

import { UserConnection, ChatRoom } from "./actors/connection";

// Export Durable Object classes
export { UserConnection, ChatRoom };

// Route WebSocket connections
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const url = new URL(request.url);

    if (url.pathname.startsWith("/ws")) {
      // Extract userId and route to user-specific DO
      const userId = url.searchParams.get("userId") || crypto.randomUUID();
      const stub = UserConnection.get(userId);
      return stub.fetch(request);
    }

    return new Response("Not Found", { status: 404 });
  }
};

Step 4: Configure Wrangler

Update wrangler.jsonc:

{
  "name": "my-verani-app",
  "main": "src/index.ts",
  "compatibility_date": "2025-11-26",

  "durable_objects": {
    "bindings": [
      {
        "class_name": "UserConnection",
        "name": "CONNECTION_DO"
      },
      {
        "class_name": "ChatRoom",
        "name": "ROOM_DO"
      }
    ]
  },

  "migrations": [
    {
      "new_sqlite_classes": ["UserConnection", "ChatRoom"],
      "tag": "v1"
    }
  ]
}

Important: Export names must match class_name in wrangler.jsonc.

Step 5: Build Your Client

import { VeraniClient } from "verani/client";

const client = new VeraniClient("ws://localhost:8787/ws?userId=alice");

// Listen for messages
client.on("chat.message", (data) => {
  console.log(`${data.from}: ${data.text}`);
});

client.on("user.joined", (data) => {
  console.log(`User ${data.userId} joined!`);
});

// Send messages
client.emit("chat.message", { text: "Hello, world!" });

// Wait for connection (optional)
await client.waitForConnection();

Step 6: Run It!

# Start the server
npm run dev
# or
wrangler dev

# In another terminal, run your client
# (or open multiple browser tabs with your client code)

That's it! You now have a working realtime chat app.

Need more help? Check out the Quick Start Guide for detailed examples.

Documentation

  • Live Documentation - Installation and quick start guide
  • Getting Started - Installation and quick start guide
  • API Reference - Complete server and client API documentation
  • Guides - Configuration, deployment, scaling, and RPC
  • Examples - Common usage patterns and code samples
  • Concepts - Architecture, hibernation, and core concepts
  • Security - Authentication, authorization, and best practices

Key Concepts

  • ConnectionDO = A Durable Object that owns ONE WebSocket per user
  • RoomDO = A Durable Object that coordinates room membership and broadcasts
  • Emit = Send messages (ctx.emit.toRoom("chat").emit("event", data))
  • Hibernation = Handled automatically, state persisted and restored

Features

Server-Side

  • Per-connection DOs: Each user gets their own Durable Object (horizontally scalable)
  • Socket.io-like API: connection.on(), ctx.emit.toRoom(), familiar patterns
  • Room coordination: RoomDOs manage membership and broadcast via RPC
  • Lifecycle hooks: onConnect, onDisconnect, onMessage for full control
  • RPC support: DO-to-DO communication for message delivery
  • Automatic hibernation: State persisted and restored automatically
  • Persistent state: Built-in support for state that survives hibernation
  • Type safety: Full TypeScript support with type inference

Client-Side

  • Automatic reconnection: Exponential backoff with configurable retry logic
  • Message queueing: Messages queued when disconnected, sent on reconnect
  • Keepalive: Built-in ping/pong to detect dead connections
  • Event-based API: Familiar on(), emit(), once(), off() methods
  • Connection state: Track connection lifecycle (connecting, connected, disconnected)

Try the Examples

See Verani in action with working examples:

git clone https://github.com/v0id-user/verani
cd verani
bun install && bun run dev

Then in another terminal, try:

# Chat room example
bun run examples/clients/chat-client.ts

# Presence tracking
bun run examples/clients/presence-client.ts

# Notifications feed
bun run examples/clients/notifications-client.ts

See the Examples README for more details.

Real-World Example

Vchats - A complete chat application built with Verani

License

ISC

Contributing

Contributions welcome! Please read our Contributing Guidelines first.

Keywords

cloudflare

FAQs

Package last updated on 16 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts