Vix.cpp v2.8.4 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.