Vix.cpp v2.8.5 is here Read the blog
Skip to content

Real-time WebSocket ​

In the previous chapter, you connected a Vix app to a database. Now you will learn WebSocket.

HTTP is request/response — the connection ends. WebSocket stays open for real-time communication.

txt
client connects → connection stays open → client sends events → server sends events → many clients receive updates

When to use WebSocket ​

Use HTTP for: CRUD APIs, page loading, authentication, normal JSON APIs.

Use WebSocket for: live messages, dashboards, presence, streaming events, notifications.

Public headers ​

cpp
#include <vix.hpp>
#include <vix/websocket.hpp>
#include <vix/websocket/AttachedRuntime.hpp>  // for HTTP + WebSocket together

Generate a WebSocket module ​

In a vix.app project, create a WebSocket module from the CLI.

bash
vix modules init
vix modules add live_chat --websocket --workflow attached

Use --name when the module name should be provided as an option instead of a positional argument.

bash
vix modules add --websocket --name live_chat --workflow attached

The workflow chooses the generated runtime shape.

WorkflowRuntime behavior
attachedHTTP and WebSocket run together.
standaloneThe module owns a WebSocket server runtime.
bridgeApplication setup is bridged to WebSocket.
clientNo runtime entry point; support code only.

Backend projects generated by Vix include a static WebSocket status panel on the home page. It connects from the browser and shows whether the WebSocket runtime is reachable.

Vix typed message model ​

json
{ "type": "chat.message", "payload": { "user": "Ada", "text": "Hello" } }

Minimal WebSocket server ​

cpp
#include <memory>
#include <vix.hpp>
#include <vix/websocket.hpp>

int main()
{
  vix::config::Config config{".env"};
  auto executor = std::make_shared<vix::executor::RuntimeExecutor>(1u);
  vix::websocket::Server ws{config, executor};

  ws.on_open([&ws](vix::websocket::Session &session){
    ws.broadcast_json(
      "system.connected",
      {"message", "A client connected"
    });
  });

  ws.on_message([&ws](vix::websocket::Session &session, const std::string &payload){
    ws.broadcast_json(
      "echo.raw",
      {"text", payload});
  });

  ws.on_typed_message([&ws](vix::websocket::Session &session,
                            const std::string &type,
                            const vix::json::kvs &payload){
    ws.broadcast_json(type, payload);
  });

  ws.listen_blocking();
  return 0;
}

HTTP + WebSocket together ​

cpp
struct BasicRuntime
{
  vix::config::Config config{".env"};
  std::shared_ptr<vix::executor::RuntimeExecutor> executor{
      std::make_shared<vix::executor::RuntimeExecutor>(1u)};
  vix::App app{executor};
  vix::websocket::Server ws{config, executor};
};

Register HTTP routes ​

cpp
static void register_http_routes(vix::App &app)
{
  app.get("/", [](vix::Request &, vix::Response &res){
    res.json({"name", "Vix HTTP + WebSocket"});
  });

  app.get("/health", [](vix::Request &, vix::Response &res){
    res.json({ "status", "ok", "service", "http-ws" });
  });
}

Register WebSocket protocol ​

cpp
static void register_ws_protocol(vix::websocket::Server &ws)
{
  ws.on_typed_message(
      [&ws](vix::websocket::Session &session,
            const std::string &type,
            const vix::json::kvs &payload)
    {
      if (type == "app.ping")
      {
        ws.broadcast_json("app.pong", {"status", "ok", "transport", "websocket"});
        return;
      }
      if (type == "chat.message")
      {
        ws.broadcast_json("chat.message", payload);
        return;
      }
      ws.broadcast_json("app.unknown", {"type", type, "message", "Unknown event type"});
  });
}

Run together ​

cpp
vix::run_http_and_ws(runtime.app, runtime.ws, runtime.executor, runtime.config.getServerPort());

Compact alternative: serve_http_and_ws ​

cpp
vix::serve_http_and_ws(".env", 8080, [](auto &app, auto &ws) {

  app.get("/", [](auto &, auto &res) {
    res.json({"framework", "Vix.cpp"});
  });

  ws.on_typed_message([&ws](auto &, const std::string &type, const vix::json::kvs &payload) {
    if (type == "chat.message") ws.broadcast_json("chat.message", payload);
  });

});
EventDirectionPurpose
chat.joinClient -> serverJoins a chat room or session.
chat.leaveClient -> serverLeaves a chat room or session.
chat.messageBoth directionsSends or receives chat content.
chat.errorServer -> clientReports a chat-related error.
app.pingClient -> serverSends a health check request.
app.pongServer -> clientReturns a health check response.
system.connectedServer -> clientConfirms the client connected.
system.disconnectedServer -> clientConfirms the client disconnected.

Validate WebSocket payloads ​

cpp
if (type == "chat.message")
{
  const std::string text = payload.get_string_or("text", "");
  if (text.empty())
  {
    ws.broadcast_json(
      "chat.error", {
        "error", "message_required",
        "message", "Message text is required"
      }
    );
    return;
  }
  ws.broadcast_json("chat.message", payload);
}

WebSocket and Nginx ​

nginx
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600;
proxy_send_timeout 3600;

Common mistakes ​

Using WebSocket for normal CRUD ​

Use HTTP for create/read/update/delete. Use WebSocket for live events only.

Broadcasting unvalidated payloads ​

Validate payloads before broadcasting.

Forgetting Nginx upgrade headers ​

Without upgrade headers, WebSocket fails through Nginx.

Not handling unknown event types ​

Always have a fallback for unknown types returning app.unknown.

What you should remember ​

HTTP is request/response. WebSocket is a long-lived real-time connection.

Vix WebSocket uses: Server, Session, on_open, on_close, on_error, on_message, on_typed_message, broadcast_json.

The core idea: use HTTP for normal API requests and WebSocket for live events that must reach clients immediately.

Next chapter ​

Next: Async runtime

Released under the MIT License.