--- title: "Getting started with cloudflarer" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting started with cloudflarer} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| label: setup #| include: false source("_vcr-setup.R") ``` cloudflarer wraps the Cloudflare v4 REST API. Most users will reach for a handful of named endpoint helpers, but anything not yet wrapped is reachable through the generic [`cf_request()`](../reference/cf_request.html) function. List endpoints return a data.frame by default (one row per record, list-columns for nested fields). The data.frames carry the `tbl_df`/`tbl` classes so callers with the tibble package loaded get tibble-style printing for free. To get the raw nested list back, pass `as_df = FALSE`. ## Setup Set a Cloudflare API token in `~/.Renviron` and restart R: ``` CLOUDFLARE_API_TOKEN=your-token-here ``` The Global API Key (legacy) also works through `CLOUDFLARE_EMAIL` + `CLOUDFLARE_API_KEY`. See `vignette("authentication")` for the full setup. Verify the connection: ```{r} #| cassette: intro-sitrep library(cloudflarer) cf_sitrep() ``` ## Discovering what you can reach ```{r} #| cassette: intro-discover cf_user() cf_list_accounts() cf_list_zones() ``` Both helpers walk every page of results automatically. Pass `max_pages` to stop early while exploring: ```{r} #| cassette: intro-zones-page cf_list_zones(max_pages = 1) ``` ## DNS records `cf_list_dns_records()` returns a data.frame; `cf_get_dns_record()`, `cf_create_dns_record()`, `cf_update_dns_record()`, and `cf_delete_dns_record()` cover the CRUD lifecycle: ```{r} #| cassette: intro-dns zones <- cf_list_zones() zone_id <- zones$id[1] records <- cf_list_dns_records(zone_id, type = "A") head(records[, c("name", "content", "proxied")]) cf_create_dns_record( zone_id, type = "A", name = "blog.example.com", content = "192.0.2.5", proxied = TRUE ) cf_update_dns_record(zone_id, "rec-1", content = "192.0.2.99") cf_delete_dns_record(zone_id, "rec-1") ``` ## Purging cache Targeted purges are preferred over wipes: ```{r} #| cassette: intro-purge cf_purge_cache(zone_id, files = c( "https://example.com/index.html", "https://example.com/style.css" )) # Or, when you really need to: cf_purge_cache(zone_id, purge_everything = TRUE) ``` ## Zone settings ```{r} #| cassette: intro-settings settings <- cf_get_zone_settings(zone_id) settings[settings$id %in% c("ssl", "cache_level", "security_level"), c("id", "value")] cf_get_zone_setting(zone_id, "ssl") ``` ## Calling endpoints the package does not wrap Every endpoint in the [Cloudflare API reference](https://developers.cloudflare.com/api/) is reachable through `cf_request()`, which builds an authenticated request for a path relative to `/client/v4`. Pipe it through the `httr2` request verbs you need, perform it, and unwrap the envelope with `cf_resp()`: ```{r} #| cassette: intro-generic cf_request("user/tokens/verify") |> httr2::req_perform() |> cf_resp() cf_request("zones/abc123/dns_records") |> httr2::req_method("POST") |> httr2::req_body_json(list( type = "A", name = "www.example.com", content = "192.0.2.1", proxied = TRUE )) |> httr2::req_perform() |> cf_resp() ``` For paginated list endpoints, pipe the request into `cf_collect()` to get the raw list, then tidy it with `cf_records_to_df()`: ```{r} #| cassette: intro-collect records <- cf_request("accounts/abc123/access/apps") |> cf_collect(per_page = 50) cf_records_to_df(records) ``` ## Error handling Both HTTP-level failures and Cloudflare envelope failures (`success: false`) raise a condition of class `cloudflarer_error`, so you can catch them with `tryCatch()`: ```{r} #| cassette: intro-error tryCatch( cf_get_zone("does-not-exist"), cloudflarer_error = function(err) { message("Cloudflare said: ", conditionMessage(err)) NULL } ) ```