Skip to content

Commit

Permalink
Merge pull request #94 from http4s/feature/ws-client-docs
Browse files Browse the repository at this point in the history
WebSocket client docs/example
  • Loading branch information
armanbilge authored Mar 20, 2022
2 parents 08f44a4 + 25dd93c commit ed59a60
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 74 deletions.
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# http4s-dom

Use http4s in your browser with Scala.js! Check out the [live example](https://http4s.github.io/http4s-dom/) in the docs.

Use http4s in your browser with Scala.js! Check out the [interactive examples](https://http4s.github.io/http4s-dom/) in the docs.

Features:

* A [`Client` implementation](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/FetchClientBuilder.html) backed by [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
* A [`Service Worker` integration](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/ServiceWorker$.html) to install your `HttpRoutes` as a [`FetchEvent` handler](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/onfetch)
* A [`Client` implementation](fetch.md) backed by [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
* A [`WSClient` implementation](websocket.md) backed by [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket$.html)
* A [`Service Worker` integration](serviceworker.md) to install your `HttpRoutes` as a [`FetchEvent` handler](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/onfetch)
* Encoders for [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)

Notably, http4s-dom can also be used to create _serverless_ apps with [Cloudflare Workers](https://workers.cloudflare.com) which have adopted the same APIs used in the browser!
Expand All @@ -17,8 +17,5 @@ Notably, http4s-dom can also be used to create _serverless_ apps with [Cloudflar

```scala
// Supports http4s 0.23.x and scala-js-dom 2.x
libraryDependencies += "org.http4s" %%% "http4s-dom" % "0.2.0"

// Or, for compatibility with scala-js-dom 1.x
libraryDependencies += "org.http4s" %%% "http4s-dom" % "0.1.0"
libraryDependencies += "org.http4s" %%% "http4s-dom" % "0.2.1"
```
66 changes: 0 additions & 66 deletions docs/README.md

This file was deleted.

6 changes: 6 additions & 0 deletions docs/directory.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
laika.navigationOrder = [
index.md
fetch.md
websocket.md
serviceworker.md
]
38 changes: 38 additions & 0 deletions docs/fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Fetch Client

The [`FetchClientBuilder`](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/FetchClientBuilder.html) creates a standard http4s [`Client`](https://http4s.org/v0.23/api/org/http4s/client/client) that is described in the [http4s documentation](https://http4s.org/v0.23/client/).

## Example

```scala mdoc:js
<div style="text-align:center">
<h3 style="padding:10px">
I'm bored.
</h3>
<button id="button">Fetch Activity</button>
<p style="padding:10px" id="activity"></p>
</div>
---
import cats.effect._
import cats.effect.unsafe.implicits._
import io.circe.generic.auto._
import org.http4s.circe.CirceEntityCodec._
import org.http4s.dom._
import org.scalajs.dom._

val client = FetchClientBuilder[IO].create

val activityElement = document.getElementById("activity")

case class Activity(activity: String)

val fetchActivity: IO[Unit] = for {
_ <- IO(activityElement.innerHTML = "<i>fetching...</i>")
activity <- client.expect[Activity]("https://www.boredapi.com/api/activity")
_ <- IO(activityElement.innerHTML = activity.activity)
} yield ()

val button = document.getElementById("button").asInstanceOf[html.Button]

button.onclick = _ => fetchActivity.unsafeRunAndForget()
```
27 changes: 27 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# http4s-dom

Use http4s in your browser with Scala.js!
Features:

* A [`Client` implementation](fetch.md) backed by [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
* A [`WSClient` implementation](websocket.md) backed by [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket$.html)
* A [`Service Worker` integration](serviceworker.md) to install your `HttpRoutes` as a [`FetchEvent` handler](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/onfetch)
* Encoders for [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)

Notably, http4s-dom can also be used to create _serverless_ apps with [Cloudflare Workers](https://workers.cloudflare.com) which have adopted the same APIs used in the browser!

## Installation

[![http4s-dom Scala version support](https://index.scala-lang.org/http4s/http4s-dom/http4s-dom/latest.svg)](https://index.scala-lang.org/http4s/http4s-dom/http4s-dom)

```scala
// Supports http4s 0.23.x and scala-js-dom 2.x
libraryDependencies += "org.http4s" %%% "http4s-dom" % "@VERSION@"

// recommended, brings in the latest client module
libraryDependencies += "org.http4s" %%% "http4s-client" % "@HTTP4S_VERSION@"

// optional, for JSON support
libraryDependencies += "org.http4s" %%% "http4s-circe" % "@HTTP4S_VERSION@"
libraryDependencies += "io.circe" %%% "circe-generic" % "@CIRCE_VERSION@"
```
7 changes: 7 additions & 0 deletions docs/serviceworker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Service Worker "Server"

The [`ServiceWorker`](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/ServiceWorker$.html) `FetchEvent` listener integrates directly with [http4s services](https://http4s.org/v0.23/service/). You can use it to run a "proxy server" as a background process in the browser that can intercept and respond to requests made by the `FetchClient`.

## Example

Coming soon!
70 changes: 70 additions & 0 deletions docs/websocket.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# WebSocket Client

The [`WebSocketClient`](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/WSClient$.html) creates a standard http4s [`WSClientHighLevel`](https://http4s.org/v0.23/api/org/http4s/client/client).

## Example

```scala mdoc:js
<div>
<h3>Send Text Frame</h3>
<input id="message" size="64" type="text">
<button id="button">Send</button>
</div>
<div style="display: flex">
<div style="flex: 50%">
<h3>Sent</h3>
<div id="sent" style="width: 100%"></div>
</div>
<div style="flex: 50%">
<h3>Received</h3>
<div id="received" style="width: 100%"></div>
</div>
</div>
---
import cats.effect._
import cats.effect.unsafe.implicits._
import org.http4s.client.websocket._
import org.http4s.dom._
import org.http4s.syntax.all._
import org.scalajs.dom._

val message = document.getElementById("message").asInstanceOf[html.Input]
val button = document.getElementById("button").asInstanceOf[html.Button]
val sent = document.getElementById("sent").asInstanceOf[html.Element]
val received = document.getElementById("received").asInstanceOf[html.Element]

val request = WSRequest(uri"wss://ws.postman-echo.com/raw")
val app = WebSocketClient[IO].connectHighLevel(request).use { conn =>

def log(e: html.Element, text: String): IO[Unit] =
IO {
val p = document.createElement("p")
p.innerHTML = text
e.appendChild(p)
()
}

val sendMessage: IO[Unit] = for {
text <- IO(message.value)
frame = WSFrame.Text(text)
_ <- conn.send(frame)
_ <- log(sent, frame.toString)
} yield ()

val receiveMessages: IO[Unit] =
conn.receiveStream
.evalTap(frame => log(received, frame.toString))
.compile
.drain

val logCloseFrame: IO[Unit] =
conn.closeFrame.get.flatMap(frame => log(received, frame.toString))

val registerOnClick = IO(button.onclick = _ => sendMessage.unsafeRunAndForget())
val deregisterOnClick = IO(button.onclick = null)

registerOnClick *> receiveMessages *> logCloseFrame *> deregisterOnClick
}

app.unsafeRunAndForget()
```

0 comments on commit ed59a60

Please sign in to comment.