Missing values in request bodies
missing-values.RmdThe rule
Real data has holes in it, and every write you send —
api_post(), api_patch(), and the
_multi and _df variants — has to say something
about them. All of them say the same thing, whatever the column
type:
| In R | On the wire | Meaning |
|---|---|---|
NA, any type |
null |
clear the field |
| absent column or list element | not sent | leave the field unchanged |
NaN, Inf, -Inf
|
error | refused before the request is sent |
NULL list element |
null |
clear the field |
You do not have to configure any of this, and it does not vary by whether you pass a tibble, a data frame, or a list.
Clearing a field, or leaving it alone
An update can mean two different things about a field, and the difference matters: clearing a recorded value is not the same as declining to touch it. The two are said differently.
To clear a field, include the column and set it to
NA:
# Drops the recorded comment
api_patch("counts", "projects/age-classes",
id = 42,
body = tibble::tibble(comment = NA_character_)
)To leave a field alone, leave the column out of the body:
# Changes age_class; the comment is untouched
api_patch("counts", "projects/age-classes",
id = 42,
body = tibble::tibble(age_class = "Sub-Adult")
)This is worth knowing when the body is something you computed rather
than typed out. A tibble that has been joined, pivoted, or filtered
often carries NA cells in columns you never meant to write,
and each of those now clears a field. Select the columns you intend to
change before sending:
body <- dplyr::select(updates, age_min_months, age_max_months)
api_patch_multi("counts", "projects/age-classes/multiple",
id = updates$id,
body = body
)If a field you clear is one the API requires, the request fails with
a 422 naming that field. That is the intended outcome: an
NA you did not mean to send is an error rather than a
change you never notice.
NaN and Inf
Division by zero, a log of zero, and a model that failed to converge all produce values JSON has no way to write down. Rather than guess at a substitute, these are refused before the request leaves your machine, and the error names the fields responsible:
api_post("counts", "estimates",
body = list(ratio = 0 / 0, density = 1 / 0)
)
#> Error: Request body contains 2 values JSON cannot represent.
#> x ratio and density are `NaN` or `Inf`.
#> i Use `NA` to clear the field, or drop it from the body to leave it
#> unchanged.Decide what the value should mean and say so: NA if the
quantity is genuinely unknown, a real number if it is not.
What changed in 1.3.0
Before 1.3.0 the wire value depended on the column’s type and on
which function you called. A numeric NA was sent as the
string "NA", a character NA as
null, and in a multi-row update an NA was
dropped from the request altogether — so the server left that field
alone and returned success, and the change you asked for silently did
not happen.
If you have code that relied on NA meaning “leave this
alone” in api_patch_multi(), it now means “clear this”.
Select the columns you intend to write, as above, and the behavior is
the same as before.
Records written with api_post_df() may also hold the
literal text "NA" in columns where a value was missing.
Those are worth finding and clearing:
api_get("counts", "estimates", filters = list(comment = "NA")) |>
parse_json2tibble()