NEWS
cloudflarer 0.0.0.9000
- Initial scaffold of the package.
New features
- Workers KV namespace management:
cf_create_kv_namespace(),
cf_rename_kv_namespace(), cf_delete_kv_namespace().
- Workers KV key-value access:
cf_list_kv_keys(),
cf_get_kv_value(), cf_put_kv_value(), cf_delete_kv_value().
- Workers KV bulk operations:
cf_put_kv_values(),
cf_delete_kv_values().
Hardening
cf_verify() no longer crashes with an NA-in-if error when
no credentials are configured. It now falls through to
/user so the underlying credential helpers raise the
documented "No Cloudflare credentials found" message.
cf_verify() honours the same credential precedence as the
request layer: explicitly-passed email/api_key now verify
against /user even when a CLOUDFLARE_API_TOKEN is present in
the environment, instead of silently verifying the env token.
cf_records_to_df() no longer silently truncates numeric data
when records mix integer and double values for the same field.
Mixed integer + double columns widen to double; otherwise
incompatible mixed scalar types fall back to a list-column.
Classed scalar values (Date, POSIXct) are kept in a
list-column rather than being stripped to bare numerics.
cf_records_to_df() validates that records is a list of
named lists and aborts with a clear message otherwise.
cf_zone_overview() now only swallows cloudflarer_error
conditions per its documented contract. Programmer errors and
other unexpected exceptions propagate instead of being silently
reduced to NULL.
- Every endpoint wrapper that interpolates an identifier into a
URL path now validates the identifier via the new internal
cf_check_id() helper, replacing confusing API or routing
errors with a single useful message.
- Boolean Cloudflare query parameters are now serialized
consistently through an internal
cf_query_bool() helper, and
boolean flags that are only sent when TRUE (for example
enabled_only, verified_only) are validated up front through
the shared cf_check_flag() helper so bad input raises the same
clear, argument-named message everywhere.
- Endpoint wrappers no longer assemble request paths with
paste0(). Each wrapper passes its path as a character vector of
segments (c("zones", zone_id, "dns_records")) that is appended
through httr2::req_url_path_append(), so every segment is
URL-encoded by httr2 rather than string-concatenated.
format_graphql_error() escapes braces after assembling the
full message, so a brace appearing in the GraphQL path
segment can no longer confuse cli's interpolation.
- Response-envelope access uses
[["result"]] instead of
$result, avoiding R's partial name matching against
result_info on the unusual case where an envelope omits
result.
- The legacy Global API Key auth path (
X-Auth-Email/
X-Auth-Key) now redacts both headers via
httr2::req_headers_redacted(), matching the protection
cf_req_auth() already gave bearer tokens. Previously, printing
or dry-running a request built with email + key credentials
leaked the Global API Key in plain text.
cf_firewall_events_top() and cf_rum_top() validate dimension
through the new internal cf_check_dimension_name() helper
before splicing it into the underlying GraphQL query. dimension
must be a single GraphQL field name and cannot collide with the
fixed metric column (events/count); previously an unvalidated
dimension was interpolated verbatim into the query text, so a
caller-controlled value (for example one forwarded from a Shiny
input by code built on top of this package) could break out of
the intended selection set and request additional fields.
- Breaking:
cf_zone_overview() now returns zone_id, since,
and until as ordinary list elements instead of object
attributes, so they're accessed the same way as every other
component (ov$zone_id rather than attr(ov, "zone_id")).
New endpoint groups
- Healthchecks:
cf_list_healthchecks(), cf_get_healthcheck(),
cf_create_healthcheck(), cf_update_healthcheck(),
cf_delete_healthcheck() for standalone (non-load-balancer)
zone healthchecks.
- SSL certificate packs:
cf_list_certificate_packs(),
cf_get_certificate_pack() for universal, advanced, and custom
zone certificates.
- Custom hostnames (SSL for SaaS):
cf_list_custom_hostnames(),
cf_get_custom_hostname(), cf_create_custom_hostname(),
cf_delete_custom_hostname().
- D1 (serverless SQL):
cf_list_d1_databases(),
cf_get_d1_database(), cf_create_d1_database(),
cf_delete_d1_database(), and cf_d1_query() for parameterised
SQL execution that returns a data.frame by default.
Core
- Composable request layer built on
httr2. cf_request() builds
an authenticated request for an endpoint and returns it; callers
pipe on the httr2 verbs they need (req_method(),
req_url_query(), req_body_json()), perform it, and unwrap the
Cloudflare envelope with cf_resp(). cf_collect() walks a
paginated list endpoint from such a request. This lets users mix
the package's helpers with plain httr2 to call anything the
package does not wrap, instead of routing everything through one
argument-heavy function.
- Two-mode authentication: API token via
CLOUDFLARE_API_TOKEN, or
legacy Global API Key via CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY.
The active mode is reported by cf_auth_mode().
- Authentication helpers:
cf_token(), cf_email(), cf_api_key(),
cf_has_auth(), cf_token_verify(), cf_verify().
- Diagnostic helper
cf_sitrep() reports the active mode and verifies
the configured credentials against the live API.
- All REST API failures that reach the API, whether HTTP-level or
envelope-level (
success: false), raise a classed
cloudflarer_error condition that can be caught with
tryCatch(). Transport-level failures (DNS, connection refused,
timeout) surface as the underlying httr2 error.
cf_collect() now walks pages with httr2::req_perform_iterative()
instead of a hand-rolled loop, gaining a progress argument
(FALSE by default) to show a progress bar across pages. Requires
httr2 >= 1.0.4.
Data frame defaults
- List endpoints (
cf_list_accounts(), cf_list_zones(),
cf_list_dns_records(), cf_list_rum_sites(),
cf_get_zone_settings()) return a data.frame by default via the
new cf_records_to_df() helper. Scalar fields become typed
columns; vector or nested fields are kept as list-columns. Pass
as_df = FALSE to get the raw nested list back.
- Returned data.frames carry the
tbl_df/tbl classes so callers
with the tibble package loaded see tibble-style printing
automatically; users without tibble simply see a data.frame.
REST endpoints
cf_user().
- Accounts:
cf_list_accounts(), cf_get_account().
- Zones:
cf_list_zones(), cf_get_zone(),
cf_get_zone_settings(), cf_get_zone_setting().
- DNS records:
cf_list_dns_records(), cf_get_dns_record(),
cf_create_dns_record(), cf_update_dns_record(),
cf_delete_dns_record().
- Cache:
cf_purge_cache() for targeted (files, hosts,
prefixes, tags) or whole-zone (purge_everything = TRUE)
invalidations.
- Page Rules:
cf_list_page_rules(), cf_get_page_rule(),
cf_create_page_rule(), cf_update_page_rule(),
cf_delete_page_rule(), plus the constructor helpers
cf_page_rule_target() and cf_page_rule_action().
- Firewall rules (classic expression-based custom rules):
cf_list_firewall_rules().
- Rulesets API (modern unified rule management):
cf_list_rulesets(),
cf_get_ruleset(), cf_list_account_rulesets(),
cf_get_account_ruleset().
- DNS query counts:
cf_dns_queries() returns daily query totals
for a zone via the GraphQL dnsAnalyticsAdaptiveGroups node
(requires Account Analytics: Read or the Global API Key).
Analytics
- Web Analytics (RUM) site catalogue:
cf_list_rum_sites(),
cf_get_rum_site().
cf_graphql() is a generic GraphQL client for Cloudflare's
Analytics GraphQL API. Variables are passed as named ...
arguments. The full response body is returned (so callers access
$data$...); non-empty errors[] raise a classed
cloudflarer_error.
- Tidy GraphQL wrappers returning data.frames:
cf_zone_requests(zone_id, since, until, by = "day"|"hour")
-- one row per time bin with requests, bytes, pageviews,
threats, uniques.
cf_cache_ratio(zone_id, since, until) -- daily cache hit
ratios for requests and bandwidth.
cf_dns_queries(zone_id, since, until) -- daily DNS query
counts (queries, uncached_queries, stale_queries).
cf_firewall_events_by_day(zone_id, since, until) -- daily
firewall event counts.
cf_firewall_events_top(zone_id, since, until, dimension, limit) -- top firewall events by action, source,
ruleId, clientCountryName, clientRequestPath, ...
cf_rum_page_views(account_id, site_tag, since, until) --
daily page-view totals for a Web Analytics site.
cf_rum_top(account_id, site_tag, since, until, dimension, limit) -- top entries by any RUM dimension
(countryName, requestPath, userAgentBrowser, ...).
- The legacy REST Zone Analytics endpoints
(
/zones/{id}/analytics/dashboard, .../analytics/colos) are
not wrapped because Cloudflare retired them in favour of the
GraphQL API.
Workers
cf_list_workers_scripts(account_id),
cf_get_workers_script(account_id, script_name).
cf_workers_invocations(account_id, since, until, script_name = NULL) returns a tidy data.frame with one row per
day per script: requests, errors, subrequests,
cpu_p50_us, cpu_p99_us.
- KV namespaces:
cf_list_kv_namespaces(account_id),
cf_get_kv_namespace(account_id, namespace_id).
Pages
cf_list_pages_projects(account_id),
cf_get_pages_project(account_id, project_name),
cf_list_pages_deployments(account_id, project_name).
R2
cf_list_r2_buckets(account_id),
cf_get_r2_bucket(account_id, bucket_name).
Turnstile
cf_list_turnstile_widgets(account_id),
cf_get_turnstile_widget(account_id, sitekey).
cf_create_turnstile_widget(account_id, name, domains, mode, bot_fight_mode, region) returns the sitekey and (one-shot)
secret.
cf_delete_turnstile_widget(account_id, sitekey).
Email Routing
cf_get_email_routing_settings(zone_id) returns the routing
state for a zone.
cf_list_email_routing_rules(zone_id, enabled_only = FALSE).
cf_list_email_routing_addresses(account_id, verified_only = FALSE) lists destination addresses (which are
account-scoped, not zone-scoped).
Tunnels (Zero Trust / cloudflared)
cf_list_tunnels(account_id, is_deleted = FALSE),
cf_get_tunnel(account_id, tunnel_id),
cf_list_tunnel_connections(account_id, tunnel_id).
Overview composite
cf_zone_overview(zone_id, since, until, account_id, site_tag)
bundles cf_zone_requests(), cf_cache_ratio(),
cf_dns_queries(), cf_firewall_events_by_day(), and (when
account_id + site_tag are supplied) cf_rum_top() into a
single named list, with a one-row summary covering the window.
Each underlying call is wrapped in tryCatch() so an individual
failure (Free-plan gating, missing permission) yields NULL
for that slot instead of aborting the whole overview. Carries
an S3 print() method that renders a one-line-per-metric
report via cli.
Documentation
- New cheatsheet vignette (
vignette("cheatsheet")) -- a single
page of copy-pasteable patterns for every endpoint surface in
the package.