Discover what you can do with an API
api_discover.RdAsks the APIs to describe themselves, answering three questions: what can I do at all, where is the thing I want, and what can I send or ask for there.
Arguments
- realm
String. A realm, or a single API's name. A realm covers every API registered to it; because each primary API is named after its realm,
"counts"means the realm and returns the superset. Required: there is no sensible default, and asking the wrong service is a confusing way to fail.- endpoint
String. Optional. An exact path returns what that endpoint accepts; a fragment returns the endpoints matching it; omitted, every endpoint is listed. Leading slashes and an
apiprefix are optional —"events","/events"and"/api/events"all resolve, as does"fit"on a service whose paths sit at the root.- refresh
Logical. Fetch the descriptions again rather than reusing the copies held for this session. Only useful after an API has been deployed.
Value
A tibble. Listing endpoints — with no endpoint, or with a
fragment that matched several:
- api
Which service the endpoint belongs to.
- endpoint
The path.
- methods
The HTTP methods allowed, so
"GET, POST"means you can read and create but not update or delete.
For one endpoint, one row per thing you can send:
- api, endpoint
Where the row came from.
- action
Which method it belongs to.
- kind
"body"for a field you send when writing;"path"for a value the URL itself needs, such as an id;"filter","include","append","sort"or"page"for ways to shape a read. Anything outside that vocabulary reports where the description says the value goes, usually"query"— which is how the plumber services declare even a POST's inputs.- name
The field or parameter, as you would pass it.
- required
Whether the API's description marks it required. See the caveat below.
- values
Accepted values, where the description enumerates them. Mostly populated for
include; blank elsewhere means unconstrained or simply undocumented, not that nothing is accepted.
Details
Called with a realm alone it lists every endpoint of every API in that realm. Called with an endpoint too it lists, per method, the fields you can send and the filters, includes, appends, sorts and pagination you can query with. Called with a fragment rather than a path it searches, so you do not have to scroll several hundred endpoints to find one.
It reads the description each API publishes rather than carrying a hard-coded list, so it cannot fall out of step, and it needs no token — you can explore before signing in.
Which APIs a realm covers
A realm's membership comes from the packages installed on this machine.
Discovery loads the namespace of every installed package that imports
spdgt.auth — it never attaches one, so your search path is untouched — and
each package's .onLoad registers the APIs and URLs it owns. Nothing is
stored here, so no address can go stale, and a package you have not
installed simply does not appear: its endpoints are ones you could not call
anyway. The visible side effect is that auth_status() may afterwards list
realms you never loaded.
A caveat on required
An API's description is written by hand alongside its validation rules, and
the two can drift. At the time of writing, POST /api/individuals describes
project_id and species as required. project_id is in fact optional and
filled in from your token; species is not a field at all, so it is dropped
here rather than offered as one. Treat required as a strong hint rather
than the contract, and let a rejected request settle any disagreement — the
validation rules are what run.
Examples
if (FALSE) { # \dontrun{
# What can I do at all? Every API in the realm.
api_discover("counts")
# Where are the project endpoints?
api_discover("counts", "project")
# What do I have to send to create one?
api_discover("counts", "/projects") |>
dplyr::filter(action == "POST", required)
# What can I include on a read?
api_discover("counts", "/surveys") |>
dplyr::filter(kind == "include")
# One service on its own, paths at the root.
api_discover("sightability", "/fit")
} # }