WebSockets
ZIO Laminar Tapir also provides support for WebSocket endpoints defined using Tapir. This allows you to create real-time, bidirectional communication channels between the client and server.
Defining WebSocket Endpoints
You can define a WebSocket endpoint using Tapir's webSocketBody directive. For example:
val echo: PublicEndpoint[
Unit,
Error,
ZioStreams.Pipe[WebSocketFrame, WebSocketFrame],
ZioStreams & WebSockets
] =
endpoint.get
.out(
webSocketBody[
WebSocketFrame,
CodecFormat.TextPlain,
WebSocketFrame,
CodecFormat.TextPlain
](
ZioStreams
)
)
.errorOut(jsonBody[Error])
.description("WebSocket echo endpoint")
This defines a WebSocket endpoint that echoes back any text frames it receives.
Then, in the same way as for regular endpoints, you can build a ZIO effect from the endpoint and handle the WebSocket communication. But as WebSockets are bidirectional, this time calling the endpoint will return a ZIO effect that produces a Pipe for handling incoming and outgoing messages.
ws <- WebsocketEndpoint.echo(())
You can then use this Pipe to send and receive messages over the WebSocket connection.
Handling WebSocket Communication
To handle WebSocket communication, you can create a Pipe that processes incoming messages and produces outgoing messages. For example, to create an echo server that sends back any message it receives:
_ <- ws(
ZStream
.fromHubWithShutdown(hub)
.tap(msg => result.zEmit(s"Sending: $msg"))
)
.runForeach {
case Text(payload, _, _) =>
result.zEmit(s"Received: $payload")
case _ => ZIO.unit
}
ws is a function that takes a ZStream of outgoing messages and returns another ZStream of incoming messages. In this example, we create a ZStream from a Hub to send outgoing messages and process incoming messages by logging them.
This setup allows you to create real-time applications using WebSockets with ZIO Laminar Tapir, leveraging the power of ZIO streams for handling bidirectional communication.
See the Full Example