Communicate with other types of APIs

️✅ 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

What is GraphQL?

  • “Open-source data query and manipulation language”
    • query = data you need
    • mutation = new data to add
    • JSON-like format
    • 1 endpoint
  • Developed by Facebook
  • Used by Facebook, GitHub, Yelp, Shopify, etc

GraphQL in R

Country data: setup

ghql_con <- ghql::GraphqlClient$new(url = "https://countries.trevorblades.com/")
qry <- ghql::Query$new()

Country data: query

qry$query("country_data",
  "query($code: [String!]) {
     countries(
       filter: {code: {in: $code}}
     ) {
       code
       name
       capital
       phone
       languages {
         code
         name
       }
     }
   }"
)
# name of this query
# define variables
#
#
# Select countries using codes
#
# fields we want for each country
  • No commas
  • No quotation marks

GraphQL response

country_codes <- list(code = c("US", "DE"))
x <- ghql_con$exec(
  qry$queries$country_data,
  country_codes
) # We created a query named "country_data"
jsonlite::fromJSON(x)
#> $data
#> $data$countries
#>   code          name         capital phone   languages
#> 1   DE       Germany          Berlin    49  de, German
#> 2   US United States Washington D.C.     1 en, English

websocket

What is websocket?

  • Alternative to HTTP
  • ws:// or wss:// (vs http:// / https://)
  • 2-way communication
  • {websocket} package

Aside: shiny & websockets

  • Sometimes see websocket-related errors from Shiny
  • Shiny UI & server communicate via a websocket connection
  • Doesn’t use {websocket}

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

websocket demo: setup

ws <- websocket::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::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()

gRPC

What is gRPC?

  • “Google Remote Procedure Call”
  • Becoming very popular
  • Good for real-time, 2-way communication
  • Uses HTTP/2 (upgraded HTTP, more socket-like)
    • Can access via {httr2}
  • “Protocol buffers” datatype

gRPC demo

From RProtoBuf paper

resp <- httr2::request("https://demo.ocpu.io/MASS/data/Animals/pb") |> 
  httr2::req_perform()
output <- httr2::resp_body_raw(resp) |> 
  protolite::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

pkg <- "tibble"
func <- "tibble"
args <- list(id = 1:3, letter = sample(letters, 3))
payload <- protolite::serialize_pb(args) # To protobuff
resp <- httr2::request("https://cloud.opencpu.org/ocpu/library") |> 
  httr2::req_url_path_append(pkg, "R", func, "pb") |> 
  httr2::req_body_raw(payload, type = "application/protobuf") |>
  httr2::req_perform()
httr2::resp_body_raw(resp) |> 
  protolite::unserialize_pb() # Back to R
#> # A tibble: 3 × 2
#>      id letter
#> * <int> <chr>
#> 1     1 n
#> 2     2 f
#> 3     3 k