Web sockets are supported through stream pipes, converting a stream of incoming messages to a stream of outgoing
messages. That’s why web socket endpoints require both the
WebSocket capabilities (see
streaming support for more information on streams).
Typed web sockets
Web sockets outputs can be used in two variants. In the first, both requests and responses are handled by codecs. Typically, a codec handles either text or binary messages, signalling decode failure (which closes the web socket), if an unsupported frame is passed to decoding.
For example, here’s an endpoint where the requests are strings (hence only text frames are handled), and the responses are parsed/formatted as json:
import sttp.tapir._ import sttp.capabilities.akka.AkkaStreams import sttp.tapir.json.circe._ import sttp.tapir.generic.auto._ import io.circe.generic.auto._ case class Response(msg: String, count: Int) endpoint.out( webSocketBody[String, CodecFormat.TextPlain, Response, CodecFormat.Json](AkkaStreams))
When creating a
webSocketBody, we need to provide the following parameters:
the type or requests, along with its codec format (which is used to lookup the appropriate codec, as well as determines the media type in documentation)
the type of responses, along with its codec format
Streamsimplementation, which determines the pipe type
By default, ping-pong frames are handled automatically, fragmented frames are combined, and close frames aren’t
decoded, but this can be customised through methods on
Raw web sockets
Alternatively, it’s possible to obtain a raw pipe transforming
import akka.stream.scaladsl.Flow import sttp.tapir._ import sttp.capabilities.akka.AkkaStreams import sttp.capabilities.WebSockets import sttp.ws.WebSocketFrame endpoint.out(webSocketBodyRaw(AkkaStreams)): PublicEndpoint[ Unit, Unit, Flow[WebSocketFrame, WebSocketFrame, Any], AkkaStreams with WebSockets]
Such a pipe by default doesn’t handle ping-pong frames automatically, doesn’t concatenate fragmented flames, and passes close frames to the pipe as well. As before, this can be customised by methods on the returned output.
Request/response schemas can be customised through
Interpreting as a sever
When interpreting a web socket endpoint as a server, the server logic needs to provide a
streaming-specific pipe from requests to responses. E.g. in akka’s case, this will be
Flow[REQ, RESP, Any].
Refer to the documentation of interpreters for more details, as not all interpreters support all settings.
Interpreting as a client
When interpreting a web socket endpoint as a client, after applying the input parameters, the result is a pipe representing message processing as it happens on the server.
Refer to the documentation of interpreters for more details, as there are interpreter-specific additional requirements.
Interpreting as documentation
Web socket endpoints can be interpreted into AsyncAPI documentation.
Determining if the request is a web socket upgrade
isWebSocket endpoint input can be used to determine if the request contains the web socket upgrade headers.
The input only impacts server interpreters, doesn’t affect documentation and its value is discarded by client
Read on about datatypes integrations.