Wist Logo

Package VersionHex Docs

Wist is a small, typed, transport-oriented realtime programming model for Gleam.

It isolates connection lifecycle and state transitions behind a transport-neutral, testable state machine. In its initial release, Wist uses WebSockets through the Mist web server as its transport layer, treating the underlying server as an interchangeable infrastructure adapter.


Mission

Wist exists to provide a clean, typed interface for persistent, bidirectional connections in Gleam.

Realtime applications often suffer from tight coupling between business logic and the network protocols carrying the data. Wist decouples these concerns by introducing a unified, transport-neutral programming model. Whether the underlying connection runs over WebSockets, WebTransport, or Server-Sent Events, your application code remains unchanged.


Vision

The vision for Wist is to serve as the minimal, elegant foundation upon which the Gleam ecosystem can build richer realtime abstractions.

Wist does not attempt to solve higher-level application architecture. Instead, it provides the low-level transport-oriented guarantees necessary to confidently build libraries such as:


Philosophy

Wist is guided by the following principles:

Out of Scope by Design

Wist intentionally does not include:

These concerns belong strictly in higher-level libraries built on top of Wist.


Mental Model

At its core, Wist models every connection as a connection-local state machine.

Connection (established)
State (constructed once via Context)
Event (inbound message or lifecycle transition)
Update (pure reducer execution)
State + Effects (new state and sequential list of output actions)

Each connection manages its own private, immutable state. When an Event occurs (such as the connection opening, a new message arriving, or the connection closing), the state machine executes an Update function. The update function transitions the state and returns a list of declarative Effects (such as sending messages or closing the connection) to be executed sequentially by the runtime.


API Walkthrough

Wist organizes its interface into eight main concepts, explained below in top-to-bottom order:

1. Context

Context carries the transport metadata captured during the initial handshake (e.g. headers, path, query parameters). It does not contain application-level domains.

2. State

State is any user-defined immutable Gleam type representing the private data held by a single connection (such as database IDs, connection metrics, or sequence counters).

3. Events

Event(message) represents inbound lifecycle or data triggers dispatched to the reducer:

4. Effects

Effect(message) represents outbound actions returned by the reducer:

5. Frames

Frame represents standard wire-level protocol frames (Text, Binary, Ping, Pong, Close).

6. Codecs

Codec(inbound, outbound) specifies how to decode a raw wire Frame into a typed application inbound message, and how to encode an outbound message back to a Frame.

7. Handler

Handler is an opaque type packaging the connection's init_state constructor and update reducer. It is instantiated using wist.handler.

8. Mist Adapter

The transport adapter (such as wist/adapters/mist) runs the opaque Handler inside the target web server's connection actor loop, executing effects and translating incoming data.


Minimal Example

Below is a complete, runnable WebSocket echo server.

import gleam/bytes_tree
import gleam/http/request.{type Request}
import gleam/http/response.{type Response}
import mist
import wist
import wist/adapters/mist as wist_mist
// 1. Define the connection-local handler
pub fn echo_handler() {
wist.handler(
init_state: fn(_ctx) { Nil },
update: fn(state, event) {
case event {
wist.Opened -> #(state, [])
wist.Message(frame) -> #(state, [wist.SendFrame(frame)])
wist.Closed(_) -> #(state, [])
wist.Failed(_) -> #(state, [])
}
},
)
}
// 2. Set up the HTTP request router
pub fn handle_request(req: Request(mist.Connection)) -> Response(mist.ResponseData) {
case req.path {
"/ws" -> wist_mist.upgrade(req, echo_handler(), wist.raw_codec())
_ -> response.new(200) |> response.set_body(mist.Bytes(bytes_tree.new()))
}
}

Walkthrough


Why Not Use Mist Directly?

Mist is an excellent, high-performance web server that provides low-level WebSocket support. Wist builds on top of Mist by introducing several core design advantages:


Semantic Guarantees

Wist provides strong guarantees to simplify application design:

  1. Isolation: Exactly one handler instance is created per connection. Connections never share state.
  2. Serialization: Events for a single connection are processed strictly sequentially. update is never executed concurrently for a single connection.
  3. Ordered Effects: Effects returned by update are guaranteed to execute in the exact order they are listed (FIFO).
  4. State Immutability: The runtime never mutates the state outside of updating it with the value returned by the reducer.

Non-Goals

Wist does not attempt to:

Maintaining a narrow, focused scope ensures Wist remains simple, fast, and easy to maintain.


Roadmap

Future development areas include:

v0.1.0 API reference

Core Types and Variants

TypePurposeDetails / Key Fields
ContextHandshake HTTP metadatapath, headers, query
CloseFrameRaw WebSocket close payloadcode, reason
CloseReasonConnection closure reasonNormal, Abnormal(String), Unknown
FrameWebSocket wire frame representationText, Binary, Ping, Pong, Close
Event(msg)Connection lifecycle event streamOpened, Message(msg), Closed(CloseReason), Failed(SocketError)
Effect(msg)Outbound reducer execution actionSend, SendFrame, CloseConnection
Codec(in, out)Frame serializer and deserializerdecode, encode function fields
Handler(state, in, out)Opaque state-machine containerCreated via wist.handler constructor

Constructors and Adapters

FunctionModulePurpose
handler(init_state, update)wistPrimary builder constructor to create a Handler
raw_codec()wistConvenience no-op codec mapping raw frames directly
upgrade(req, handler, codec)wist/adapters/mistUpgrades a Mist HTTP connection to Wist WebSocket

License

MIT License - Copyright (c) 2026 Antonio Ognio

Made with ❤️ from 🇵🇪. El Perú es clave 🔑.