How else can I communicate with APIs from R?

Learning objectives:

  • Fetch data from GraphQL APIs.
  • Fetch data from websocket APIs.
  • Fetch data from gRPC APIs.
library(ghql)
library(jsonlite)
library(dplyr)
library(httr2)
library(websocket)
library(protolite)
# library(RProtoBuf)

GraphQL

  • Current landscape feels like “I use GraphQL in other languages, and want it to technically be possible to do so from R.”
  • Queries: ghql
    • Demo on next slide
  • Server side: gqlr (more in later chapter)

GraphQL setup

ghql_con <- GraphqlClient$new(
  url = "https://api.github.com/graphql",
  headers = list(Authorization = paste0("Bearer ", Sys.getenv("GITHUB_PAT")))
)
ghql_con$load_schema()
qry <- Query$new()

GraphQL query

qry$query('mydata', '{
  repositoryOwner(login:"jonthegeek") {
    repositories(first: 5, orderBy: {field:PUSHED_AT,direction:DESC}, isFork:false) {
      edges {
        node {
          name
          stargazers {
            totalCount
          }
        }
      }
    }
  }
}')

GraphQL response

x <- ghql_con$exec(qry$queries$mydata) # We created a query named "mydata"
jsonlite::fromJSON(x)
#> $data
#> $data$repositoryOwner
#> $data$repositoryOwner$repositories
#> $data$repositoryOwner$repositories$edges
#>                   node.name node.totalCount
#> 1                     wapir              17
#> 2                    zoomer               3
#> 3                rstats.fyi               0
#> 4                 tidyslack               0
#> 5 jonthegeek.r-universe.dev               0

websocket review

  • Alternative to HTTP
  • ws:// or wss://
  • 2-way communication
  • {websocket} package

Aside: shiny & websockets

  • You might see websocket-related errors in Shiny
  • Shiny UI & server communicate via a websocket connection
  • More directly managed via {httpuv} & TypeScript code
  • Beyond the scope of this book

websocket demo: setup

ws <- WebSocket$new("ws://echo.websocket.events/", autoConnect = FALSE)
ws$onMessage(\(event) {
  now <- format(Sys.time(), digits = 0)
  cat("Client got msg:", event$data, "at", now, "\n")
})

websocket demo

ws$connect()
#> Client got msg: echo.websocket.events sponsored by Lob.com at 2023-11-15 2023-11-15 08:28:04
# (can do other things in console now)
1
#> [1] 1
ws$send("hello")
#> Client got msg: hello at 2023-11-15 08:28:10
ws$close()

websocket: toward usefulness

ws_counter <- 1
ws2 <- WebSocket$new("ws://echo.websocket.events/", autoConnect = FALSE)
ws2$onMessage(\(event) {
  ws_counter <<- ws_counter + 1 # Add 1 to global ws_counter var 
  cat(ws_counter, "\n")
})
ws_counter
#> 1
ws2$connect()
#> 2
ws2$send("update")
#> 3
ws2$send("update again")
#> 4
ws2$close()

websocket usecases

  • News/Message feeds
    • Display new content as it comes in
  • Messaging
    • Send and receive ~simultaneously without new connections
  • Multi-player games
  • Collaborative editing
  • Real-time dashboards

gRPC review

  • Google Remote Procedure Call
  • Becoming very popular
  • Good for real-time, 2-way communication
  • Uses HTTP/2 (more socket-like)

gRPC: What is it really?

  • HTTP/2 (upgraded HTTP)
    • Can access via {httr2}
  • “Protocol buffers” datatype
    • Fully implemented in {RProtoBuf} package
    • {protolite} for basics

gRPC demo

From RProtoBuf paper

resp <- request("https://demo.ocpu.io/MASS/data/Animals/pb") |> 
  req_perform()
output <- resp_body_raw(resp) |> 
  unserialize_pb() # This is the important part
identical(output, MASS::Animals)
#> [1] TRUE
head(output)
#>                     body brain
#> Mountain beaver     1.35   8.1
#> Cow               465.00 423.0
#> Grey wolf          36.33 119.5
#> Goat               27.66 115.0
#> Guinea pig          1.04   5.5
#> Dipliodocus     11700.00  50.0

gRPC demo2: R via API

args <- list(n = 5, mean = 100)
payload <- serialize_pb(args, connection = NULL)
resp <- request("https://cloud.opencpu.org/ocpu/library") |> 
  req_url_path_append("stats", "R", "rnorm", "pb") |> 
  req_body_raw(payload, type = "application/protobuf") |>
  req_perform()

resp_body_raw(resp) |> 
  unserialize_pb()
#> [1] 102.61489 101.70467  99.12647  99.85958 100.47736

Meeting Videos

Cohort 1

Meeting chat log
LOG