WebSockets
WebSockets are HTTP upgrade routes declared with sdk.WS.
Routes struct { Socket sdk.WSWith[ReadPolicy] `path:"/:projectId/socket"`}The handler receives sdk.Ctx and sdk.WebSocket:
func (p *Projects) Socket(ctx sdk.Ctx, socket sdk.WebSocket) error { for { message, err := socket.Read() if err != nil { return ctx.Errors().Wrap(err, "reading websocket message") }
if err := socket.Write(sdk.WebSocketMessage{ Type: sdk.WebSocketText, Data: message.Data, }); err != nil { return ctx.Errors().Wrap(err, "writing websocket message") } }}Socket Contract
Section titled “Socket Contract”sdk.WebSocket exposes:
Context() context.ContextNative() anySubprotocol() stringRead() (sdk.WebSocketMessage, error)Write(sdk.WebSocketMessage) errorClose(sdk.WebSocketCloseCode, string) error
Native() returns the driver-owned socket for deliberate driver-specific
behavior.
Official HTTP drivers support writing text and binary messages through
socket.Write. Use socket.Close(code, reason) for close frames.
Portable socket handlers treat text and binary as the normal application
message types. The Fiber driver can surface close, ping, and pong message types
from its underlying socket. The standard driver maps non-text and non-binary
reads to WebSocketMessageUnknown. Writing ping, pong, close, or unknown
messages through socket.Write returns an error in the official drivers; use
socket.Close for close frames or socket.Native() when the handler
deliberately opts into driver-specific control-frame behavior.
Middleware
Section titled “Middleware”HTTP middleware policies can run before the upgrade. Put auth, tenant selection, and rate-limit decisions there.
For HandleHTTP middleware, ctx.Next() is the point where Anvil continues to
the upgrade and socket handler. Returning without calling ctx.Next() stops
the upgrade path. If the middleware returned a body before the socket was
accepted, the driver writes a normal HTTP response. AfterHTTP and
OnHTTPError run after the upgrade attempt or socket handler returns.
Read HTTP Middleware for the method shapes and policy placement rules.
Errors
Section titled “Errors”WebSocket errors go through the same global error pipeline.
Before the upgrade succeeds, the HTTP driver can still write a normal mapped HTTP error response. After the upgrade succeeds, there is no HTTP response left to write. The driver maps the error, publishes the error event, and closes the socket. External reporting belongs in global error observers or protocol middleware.
Drivers recover panics and publish error events so one failed socket does not stop the server.
The standard-library driver uses github.com/coder/websocket underneath.
socket.Native() returns *websocket.Conn. The Fiber driver uses
github.com/gofiber/contrib/websocket; socket.Native() returns
*websocket.Conn from that package.
Driver-specific upgrade behavior:
- The standard driver only checks WebSocket routes when the request contains
WebSocket upgrade headers. The socket library validates the handshake during
accept. A normal HTTP request to a WebSocket-only path returns
404unless another HTTP route matches that path. - The Fiber driver registers WebSocket routes as
GETroutes. A request that reaches that path without a WebSocket upgrade receives426 Upgrade Required.