API

Macros

RestClient.@globalconfigMacro
@globalconfig config [auth=scheme | auth=(scheme, policy)]

Define globalconfig for the current module as config, and optionally a module-wide authentication default applied to endpoints defined with @endpoint.

This avoids having to write the slightly awkward RestClient.globalconfig(::Val{@__MODULE__}) = config by hand. Similarly, the optional auth clause defines a RestClient.globalauthdefault(::Val{@__MODULE__}) method. The auth argument is may take one of two forms:

  • auth = scheme: the AuthScheme endpoints in this module use (e.g. BearerAuth()); the request's key is applied to it. With no policy given, the policy is :off — declare the scheme, but send it only where an endpoint opts in.
  • auth = (scheme, policy): also set the authpolicy, one of:
    • :off — never send auth (the scheme is declared but unused here);
    • :optional — send the key if one is set, but do not require it;
    • :required — send the key, and reject a request that has none before sending.

See also: globalconfig, RequestConfig, authscheme.

Examples

@globalconfig RequestConfig("https://api.example.com")
# Send a Bearer token on every endpoint when a key is set:
@globalconfig RequestConfig("https://api.example.com") auth=(BearerAuth(), :optional)
# Require a key on every endpoint (mark public ones `:noauth`):
@globalconfig RequestConfig("https://api.example.com") auth=(BearerAuth(), :required)
source
RestClient.@endpointMacro
@endpoint struct ... end
@endpoint [func(...)] -> [struct ... end] -> [payload] -> url -> resulttype

Generate an endpoint implementation from a concise shorthand form.

This macro can be applied to either an endpoint struct definition or a chain of arrow forms joined by ->. These arrow forms represent the flow of information:

  1. The creation of a request, from a function call (optional)
  2. The struct used to represent the request data (optional, autogenerated from the function call)
  3. Any payload provided with the request (if applicable)
  4. The page URL the request is directed to (relative to the base URL)
  5. The type that the response is expected to correspond to

For a basic API, this can be as simple as:

@endpoint israining(city::String) -> "checkrain/{city}" -> Bool

Using this macro is equivalent to implementing a plain struct, along with these endpoint methods:

In our @endpoint israining() ... example, in addition to defining the israining function and an (implicit) IsrainigEndpoint struct, the macro will generate the implementations urlpath(rain::IsrainingEndpoint) = "checkrain/$(rain.city)" and responsetype(::IsrainingEndpoint) = Bool.

In addition to applying @endpoint to a sequence of arrow forms you can also apply it to a struct that starts with a special line of non-struct arrows.

For instance, our israining() example could be alternatively written as:

@endpoint struct IsrainingEndpoint
    israining(city) -> "checkrain/{city}" -> Bool
    city::String
end

This generates the exact same code as the initial example form.

For more information on how each component of the shorthand form is interpreted, as well as more complex examples, see the extended help section.

See also: urlpath, parameters, payload, responsetype, Request, globalconfig, @globalconfig.

Extended help

Function forms

Should you chose to include a function call as the first arrow form, a function definition with a matching signature will be generated. This requires globalconfig(::Val{@__Module__}) to be defined, as is done by @globalconfig.

Without a struct form, the arguments of the function will be used to generate a struct with a derived name ($(titlecase(funcname))Endpoint), and populate its fields.

With a struct form, all arguments that match a field name will inherit the field's type and default value . All the fields of the struct not present in the signature will be added as keyword arguments, with Union{..., Nothing} typed fields defaulting to nothing. This makes function forms an easy way to prescribe positional, mandatory arguments with the flexibility to fully specify the endpoint with keyword arguments.

Struct forms

The endpoint struct can be given explicitly, or generated from the function call. For instance, the function call api(target::String; count::Int=1) will generate this struct form:

@kwdef struct ApiEndpoint
    target::String
    count::Int = 1
end

When autogenerated, or not specified, the endpoint supertype is selected from AbstractEndpoint, SingleEndpoint, ListEndpoint, and StreamEndpoint based on the supertype of the output form (a Stream output selects StreamEndpoint; see the streaming manual).

If @endpoint is directly applied to a struct, the first line of the struct is removed and parsed as a series of arrow forms.

@endpoint ApiEndpoint
    api(target; count) -> ...
    target::String
    count::Int = 1
end

This makes no difference to the generated code, and is purely a matter of personal preference (I prefer explicit structs for more complex forms).

Payload forms

HTTP methods like :post, :put, and :patch include a content payload (also referred to as the request body). The payload can be specified with a payload form that names a field of the endpoint struct, names a global variable, or is an expression is evaluated to generate the payload value (with the current endpoint available through the anaphoric variable self).

Unless otherwise specified, an endpoint that includes a payload is assumed to be a :post request (:get without).

@endpoint upload(content::String) -> content -> "create" -> Status

This is equivalent to the explicit HTTP method form:

@endpoint upload(content::String) -> content -> :post("create") -> Status

In this example content is a String, and so a :post request is made to "$BASEURL/create" with content as the payload/body. More complex types are encoded according with writepayload according to dataformat.

URL forms

The endpoint URL is represent by a string, relative to the base URL of the API (a leading / is stripped). Such paths usually consist of a path component, and optionally a query component.

"some/path/for/my/api?query=param&other=value"

Within this endpoint URL, you can include references to fields of the struct (or global variables) by surrounding them with curly braces.

"page/{somefield}?param={another}&{globalvar}=7"

Parameters with a value of nothing are omitted from the query string.

For convenience, a parameter by the same name as the field can be referred to by the field name alone (e.g. ?{var} instead of ?var={var}).

In more complex cases, arbitrary Julia code can be included in the curly braces. This code will be evaluated with the endpoint value bound the the anaphoric variable self as well as config for the request configuration.

"page/{if self.new \"new\" else \"fetch\" end}/{id}"

If that's not enough, you can also use an arbitrary expression in place of the endpoint string:

if self.create "pages/create/" else "pages/fetch/id/" * self.id end

When using expression instead of a string though, curly braces are not interpreted as field references.

Specifying the HTTP method

By default, it is guessed whether the API expects a :get or :post request based on the presence or absence of an input form. This can be explicitly specified by wrapping the URL form in an HTTP method symbol, for example:

:head("existence/{entity}")

Response type forms

It is sensible to parse the raw response into a more informative Julia representation. The response type specifies what type the response can be parsed to, which is performed by interpretresponse according to dataformat.

Often the result type will name a struct defined with [@jsondef].

The parsed result can be reinterpreted into another form with postprocess. By default, ListResponse-type responses from a ListEndpoint are restructured into a List to facilitate paging.

Examples

@endpoint struct ShuffleEndpoint <: SingleEndpoint
    "deck/{deck}/shuffle?remaining={ifelse(self.remaining, '1', '0')}" -> Deck
    deck::String
    remaining::Bool
end

This is equivalent to defining the struct by itself, and then separately defining the three basic endpoint methods.

struct ShuffleEndpoint <: SingleEndpoint
    deck::String
    remaining::Bool
end

RestClient.urlpath(shuf::ShuffleEndpoint) =
    "deck/$(shuf.deck)/shuffle"
RestClient.parameters(shuf::ShuffleEndpoint) =
    ["remaining" => string(shuf.remaining)]
RestClient.responsetype(shuf::ShuffleEndpoint) = Deck
source
RestClient.@jsondefMacro
@jsondef [:noshow] [kind] struct ... end

Define a struct that can be read from and written as JSON.

This macro conveniently combines the following pieces:

  • @kwdef to define keyword constructors for the struct.
  • Custom Base.show method to show the struct with keyword arguments, omitting default values (suppress with the :noshow option).
  • Registration of JSON field-name mapping with the active JSON backend.
  • RestClient.dataformat to declare that this struct is JSON-formatted.

Note the name."json_field" syntax demonstrated in the examples, that allows for declaration of the JSON object key that should be mapped to the field.

The optional kind argument selects the JSON representation, defaulting to Struct. Both backends support Struct, Dict, Array, Vector, Nothing, and String; for String, the user must define Base.string and a T(::AbstractString) constructor. The JSON3 backend additionally supports Number and Bool; with the JSON.jl backend these require user-defined StructUtils.lift / StructUtils.lower methods.

For an open-ended JSON object or array, a struct wrapping a single AbstractDict (kind=Dict) or AbstractVector (kind=Array/Vector) field decodes the whole payload into that field (recursively) and wraps it. Any other shape declares only dataformat, leaving the backend integration to the user.

@jsondef Dict struct Translations
    entries::Dict{String, String}   # {"en": "Hello", "fr": "Bonjour"} → Translations(...)
end
Soft JSON dependency

This macro is implemented in package extensions, and so requires either JSON or JSON3 to be loaded before it can be used. If both are loaded, the backend is chosen based on which of JSON or JSON3 is using'd in the calling module.

Examples

@jsondef struct DocumentStatus
    exists::Bool  # Required, goes by 'exists' in JSON too
    status."document_status"::Union{String, Nothing} = nothing
    url."document_url"::Union{String, Nothing} = nothing
    age::Int = 0  # Known as 'age' in JSON too, defaults to 0
end
source
RestClient.@xmldefMacro
@xmldef struct ... end

Define a struct that can be used with XML.

This macro conveniently combines the following pieces:

  • XML deserialization
  • @kwdef to define keyword constructors for the struct.
  • RestClient.dataformat to declare that this struct is XML-formatted.

Note the name."xpath" syntax demonstrated in the examples, that allows for declaration of the (simple) XPath that should be used to extract the field. The subset of supported XPath components are:

  • nodetag to extract all immediate children with a given tag name
  • relative/node/paths
  • * to extract all children
  • text() to extract the text content of a node
  • @attr to extract the value of an attribute
  • nodetag[i] to extract the i-th child of type nodetag
  • nodetag[last()] to extract the last child of type nodetag
Soft XML dependency

This macro is implemented in a package extension, and so requires XML to be loaded before it can be used.

Examples

@xmldef struct DocumentStatus
    exists."status/@exists"::Bool
    status."status/text()"::String
    url."status/@url"::Union{String, Nothing} = nothing
    age."status/@age"::Int
end
source
RestClient.@formdefMacro
@formdef struct ... end

Define a struct that serialises as FormFormat. Supports the name."form_key"::Type syntax for remapping a field's form name.

Example

@formdef struct Login
    username::String
    password::String
    remember_me."remember-me"::Bool = false
end
source
RestClient.@multipartdefMacro
@multipartdef struct ... end

Define a struct that serialises as MultipartFormat. Each field is encoded with its type's own dataformat: @jsondef fields become JSON parts, Vector{UInt8} / IO fields become octet-stream parts, others become text parts.

Example

@multipartdef struct Upload
    file::Vector{UInt8}
    metadata::Metadata    # `@jsondef struct Metadata ... end`
    note::Union{String, Nothing} = nothing
end
source

Request types

RestClient.RequestType
Request{kind, E<:AbstractEndpoint}

A request to an API endpoint, with a specific configuration.

This is the complete set of information required to make a kind HTTP request to an endpoint E. This consists of the request configuration and target endpoint itself. The kind and endpoint::E are put as type parameters so that they may participate in method dispatch.

See also: AbstractEndpoint, RequestConfig, perform.

Examples

julia> Request{:get}(RequestConfig(...), MyEndpoint(...))
Request{:get, MyEndpoint}(RequestConfig(...),MyEndpoint(...))

Data flow

         ╭─╴config╶────────────────────────────╮
         │     ╎                               │
         │     ╎        ╭─▶ responsetype ╾─────┼────────────────┬──▶ dataformat ╾───╮
Request╶─┤     ╰╶╶╶╶╶╶╶╶│                      │                ╰─────────╮         │
         │              ├─▶ urlpath ╾────╮     │      ╓┄┄*debug*┄┄╖       │  ╭──────╯
         │              │                ├──▶ url ╾─┬─━─▶ request ┊ ╭─▶ interpret ╾──▶ data
         ├─╴endpoint╶───┼─▶ parameters ╾─╯          │ ┊      ┠────━─┤                   │
         │              │                           ├─━──▶ cache  ┊ │             ╭─────╯
         │              ├─▶ parameters ╾────────────┤ ┊           ┊ ╰─────────╮   │
         │              │                           │ ╙┄┄┄┄┄┄┄┄┄┄┄╜        postprocess ╾──▶ result
         │             *╰─▶ payload ─▶ writepayload╶╯                           │
         │                    ╰─▶ dataformat ╾╯                                 │
         ╰─────────┬────────────────────────────────────────────────────────────╯
                   ╰────▶ validate (before initiating the request)

 * Only for POST requests   ╶╶ Optional first argument
source
RestClient.RequestConfigType
RequestConfig

The general configuration for a request to the API, not tied to any specific endpoint.

At a minimum, this holds the base URL of the API and a lock for handling rate-limiting.

Other fields are optional, but may be useful for changing the way requests are performed or handling authentication. The following additional fields can be provided as keyword arguments:

  • key::Union{Nothing, String}: The secret used to authenticate requests. How it is presented on the wire is declared by endpoints.
  • timeout::Float64: The timeout for the request, in seconds. This is passed to Downloads.download and defaults to Inf.
  • cache::Bool: Whether to cache the response, using lifetime information from standard HTTP response headers. Defaults to true.

See also: @globalconfig, Request.

Examples

julia> RequestConfig("https://api.example.com", timeout = 5, cache = false)
RequestConfig("https://api.example.com"; timeout = 5.0, cache = false)

julia> RequestConfig("https://api.example.com", key = ENV["API_SECRET_KEY"])
RequestConfig("https://api.example.com"; key = "*****", cache = true)
source
RestClient.AbstractEndpointType
AbstractEndpoint

Abstract supertype for API endpoints.

Usually you will want to subtype either SingleEndpoint or ListEndpoint, which share the same interface as AbstractEndpoint but have additional semantics.

Cross-cutting concerns (auth, logging, shared validation) are best expressed by defining an API-specific abstract subtype and attaching generic methods to it.

Interface

urlpath([config::RequestConfig], endpoint::AbstractEndpoint) -> String
headers([config::RequestConfig], endpoint::AbstractEndpoint) -> Vector{Pair{String, String}}
parameters([config::RequestConfig], endpoint::AbstractEndpoint) -> Vector{Pair{String, String}}
responsetype(endpoint::AbstractEndpoint) -> Union{Type, Nothing}
validate([config::RequestConfig], endpoint::AbstractEndpoint) -> Bool
postprocess([response::Downloads.Response], request::Request, data) -> Any

All of these functions but urlpath have default implementations.

See also: Request, dataformat, interpretresponse.

source

Response types

RestClient.SingleResponseType
SingleResponse{T}

Abstract supertype for responses that contain a single T item and (optionally) metadata.

Interface

Subtypes of SingleResponse may need to define these two methods:

contents(single::SingleResponse{T}) -> T
metadata(single::SingleResponse{T}) -> Dict{Symbol, Any}

Both have generic implementations that are sufficient for simple cases.

source
RestClient.ListResponseType
ListResponse{T}

Abstract supertype for responses that contain a list of T items and (optionally) metadata.

Interface

Subtypes of ListResponse may need to define these two methods:

contents(list::ListResponse{T}) -> Vector{T}
metadata(list::ListResponse{T}) -> Dict{Symbol, Any}

Both have generic implementations that are sufficient for simple cases.

source

Errors

A failed perform raises one of these. A request that does not validate raises a MalformedRequest before anything is sent; an unsuccessful HTTP status raises a ResponseError carrying the server's response body. A transport failure (DNS, connection refused, TLS, timeout) surfaces as the underlying Downloads.RequestError.

RestClient.ResponseErrorType
ResponseError <: Exception

An exception type for requests that complete but return an unsuccessful HTTP status (outside 200:299).

Unlike a bare transport failure, the server's response is available: response holds the status and headers, and body holds the raw response body, which APIs routinely use to explain why a request failed.

See also: perform, Request.

source

Streaming

RestClient.StreamType
Stream{T, F}

A lazily-consumed stream of elements of type T, framed by F (a symbol, e.g. :SSE/:NDJSON). A peer of Single/List, selected through an endpoint's output type; F defaults to :SSE.

Iterate it to pull decoded elements as bytes arrive (blocking per frame). It is a plain value: close it from any task to end iteration cleanly (see the manual on cancellation), and a dropped, undrained stream is reclaimed by its finalizer.

See also: isstreamend.

source
RestClient.StreamEndpointType
StreamEndpoint <: AbstractEndpoint

Supertype for endpoints returning a Stream. The @endpoint macro selects it for any Stream-typed output; perform dispatches on it to the streaming request path.

source
RestClient.SSEventType
SSEvent{T}

A Server-Sent Event with payload data::T (undecoded bytes off the wire, or the decoded T), the dispatching event type, and the sticky last-event-id.

Destructures as (event, data) or (event, data, id), and exposes each as a field.

source
RestClient.isstreamendFunction
isstreamend(endpoint::AbstractEndpoint, frame) -> Bool

Whether frame terminates the stream from endpoint. Applied to the undecoded public frame before it is decoded or emitted, so a terminator is never seen by the consumer. The default is false (stop only at EOF, correct for any stream); a client library defines one method per API over the frame's public accessors:

isstreamend(::ChatEndpoint, fr::SSEvent)     = fr.data == codeunits("[DONE]")  # OpenAI
isstreamend(::MessagesEndpoint, fr::SSEvent) = fr.event == "message_stop"      # Anthropic
source

Framing extension interface

These methods define a stream framing. The shipped :SSE and :NDJSON framings cover the common cases; a client library implementing a new framing defines these.

RestClient.readframeFunction
readframe(bs::IO, state, fmt::StreamFormat) -> frame | nothing

Read one complete frame from bs, returning the framing's public frame with its undecoded payload bytes, or nothing at clean EOF. state is framestate's value, reused across calls. Dispatched on fmt's framing.

The read blocks on bs, reassembling a frame split across network reads via Base's copyuntil/read; it touches no stream internals.

source
RestClient.framestateFunction
framestate(fmt::StreamFormat)

The reusable per-stream state readframe threads across calls. Defaults to a fresh IOBuffer (the reused scratch for delimiter framings); a framing that carries state across frames overrides it.

source
RestClient.framedataFunction
framedata(frame) -> payload

Extract a frame's payload: the field whose type is the frame's type parameter. A framing whose payload is located differently can override this.

source
RestClient.remakeFunction
remake(frame, data) -> frame

Return a copy of frame with its payload replaced by data (re-typed to data's type), preserving the other fields. This is the decode step: {bytes} → {T}. The payload field is located as in framedata.

source
RestClient.elementpayloadFunction
elementpayload(::Type{Elt}) -> Type

The decode target for a stream element type. Defaults to Elt; a framing whose element type wraps its payload (e.g. SSEvent{T}T) overrides it.

source

Request interface

RestClient.globalconfigFunction
globalconfig(::Val{::Module}) -> RequestConfig

Return the global configuration for the given module.

This is used in @endpoint generated API functions.

See also: @globalconfig, RequestConfig.

Warning

Be careful not to accidentally define this function in a way that generates a new RequestConfig every time it is called, as this will cause state information (like rate limits) to be lost between requests.

source
RestClient.validateFunction
validate([config::RequestConfig], endpoint::AbstractEndpoint) ->
    Union{Nothing, AbstractString, AbstractVector{<:AbstractString}}

Check if the request to endpoint according to config is valid.

The return value determines whether the request proceeds and, if not, what problem to report:

  • nothing or String[] means the request is valid and should proceed
  • a string or vector of strings means the request is invalid and should be aborted, with the string(s) as the reason(s)

This is also the appropriate place to emit warnings about non-critical potential issues with the request, using @warn or @debug.

The default implementation returns nothing (proceed).

Note

Part of the AbstractEndpoint interface.

source
RestClient.performFunction
perform(req::Request{kind}) -> Any

Validate and perform the request req, and return the result.

The specific behaviour is determined by the kind of the request, which corresponds to an HTTP method name (:get, :post, etc.).

See also: Request.

source
RestClient.urlpathFunction
urlpath([config::RequestConfig], endpoint::AbstractEndpoint) -> String

Return the name of the page for the given endpoint.

This is combined with the base URL and parameters to form the full URL for the request.

Note

Part of the AbstractEndpoint interface.

source
RestClient.headersFunction
headers([config::RequestConfig], endpoint::AbstractEndpoint) -> Vector{Pair{String, String}}

Return headers for the given endpoint.

The default implementation returns an empty list.

Default Content-Type

When no Content-Type header is provided and mimetype is defined for the format, the Content-Type header is set to the value from mimetype.

source
RestClient.parametersFunction
parameters([config::RequestConfig], endpoint::AbstractEndpoint) -> Vector{Pair{String, String}}

Return URI parameters for the given endpoint.

This are combined with the endpoint URL to form the full query URL.

The default implementation returns an empty list.

Note

Part of the AbstractEndpoint interface.

source
RestClient.payloadFunction
payload([config::RequestConfig], endpoint::AbstractEndpoint) -> Any

Return the payload for the given endpoint.

This is used for POST requests, and is sent as the body of the request.

Note

Part of the AbstractEndpoint interface.

source
RestClient.authschemeFunction
authscheme(endpoint::AbstractEndpoint) -> AuthScheme

Return how endpoint authenticates: the AuthScheme describing where the request's secret (config.key) is placed on the wire. The library applies it to key automatically, so a standard scheme needs no headers method.

The default is NoAuth. Declare a scheme once per API, typically on a shared endpoint supertype:

RestClient.authscheme(::MyAPIEndpoint) = BearerAuth()          # Authorization: Bearer <key>
RestClient.authscheme(::OtherEndpoint) = HeaderAuth("X-API-Key")

For a scheme the provided ones do not cover (request signing, OAuth flows), read config.key in a headers method directly instead.

Note

Part of the AbstractEndpoint interface.

source
RestClient.authpolicyFunction
authpolicy(endpoint::AbstractEndpoint) -> Symbol

How endpoint uses authentication, one of:

  • :off — never send auth (the default);
  • :optional — apply the authscheme to the request's key if one is set, but do not require it;
  • :required — apply it, and reject a request whose config.key is nothing during validation, before sending, rather than relying on the server to return 401.

The @endpoint macro sets this from the module's @globalconfig auth= default, or per endpoint via an :auth (required), :authoption (optional), or :noauth (off) marker.

source

Authentication

An endpoint declares which scheme it authenticates with via an authscheme method, and whether a key is off/optional/required via authpolicy; the secret itself is the RequestConfig's key. Both are usually set in one place with @globalconfig's auth= option.

RestClient.AuthSchemeType
AuthScheme

Supertype for authentication schemes: how an API presents its secret on the wire. A scheme holds only API-specific shape (e.g. a header name), never the secret — the secret is the key of a RequestConfig. Declare a scheme for an endpoint with authscheme, and it is applied to key on every request.

The provided schemes are BearerAuth, BasicAuth, HeaderAuth, QueryAuth, and NoAuth (the default).

Non-standard authentication schemes can be handled by adjusting headers and parameters of endpoints on a case-by-case basis.

source
RestClient.BearerAuthType
BearerAuth() <: AuthScheme

Bearer-token authentication: the key is sent as Authorization: Bearer <key>. The common scheme for OAuth2 access tokens and most modern token-based APIs.

source
RestClient.BasicAuthType
BasicAuth() <: AuthScheme

HTTP Basic authentication (RFC 7617): the key (a "username:password" string) is sent as Authorization: Basic <base64>. Set key = "user:pass" on the config.

source
RestClient.HeaderAuthType
HeaderAuth(name) <: AuthScheme

The key is sent in the named request header, e.g. HeaderAuth("X-API-Key"). For APIs that authenticate with a custom header rather than Authorization.

source
RestClient.QueryAuthType
QueryAuth(name) <: AuthScheme

The key is sent as the named URL query parameter, e.g. QueryAuth("api_key"). For APIs that authenticate via the query string. Prefer a header scheme where the API allows it: query parameters are more likely to be logged by proxies and servers.

source

Response interface

RestClient.responsetypeFunction
responsetype(endpoint::AbstractEndpoint) -> Type

Return the type of the response for the given endpoint.

Together with dataformat, this is used to parse the response.

If Vector{UInt8} (the default implementation), the response data is provided raw.

Note

Part of the AbstractEndpoint interface.

source
RestClient.postprocessFunction
postprocess([response::Downloads.Response], request::Request, data) -> Any

Post-process the data returned by the request.

There are three generic implementations provided:

  • For SingleEndpoint requests that return a SingleResponse, the data is wrapped in a Single object.
  • For ListEndpoint requests that return a ListResponse, the data are wrapped in a List object.
  • For all other endpoints, the data is returned as-is.
Note

Part of the AbstractEndpoint interface.

source
RestClient.contentsFunction
contents(response::SingleResponse{T}) -> T

Return the content of the response.

source
contents(response::ListResponse{T}) -> Vector{T}

Return the items of the response.

source
RestClient.metadataFunction
metadata(response::SingleResponse) -> Dict{Symbol, Any}
metadata(response::ListResponse) -> Dict{Symbol, Any}

Return metadata for the given response.

The default implementation returns an empty dictionary.

source

Pagination interface

RestClient.nextpageFunction
nextpage(response::List) -> Union{List, Nothing}

Fetch the next page of results after response.

If there are no more pages, or this method is not available for the given endpoint, return nothing.

The generic List implementation returns nothing.

See the extended help for implementation suggestions.

Extended help

There are two more methods that can be implemented to support pagination:

nextpage(request::Request) -> Union{Request, Nothing}
nextpage(endpoint::AbstractEndpoint) -> Union{AbstractEndpoint, Nothing}

When implementing this method, a good approach to follow is:

  1. Implement thispagenumber and remainingpages. These are both optional, but pass through the information to the List display method, which is nice to have.
  2. Consider how the request should be modified to fetch the next page, and accordingly implement nextpage for either the endpoint, request, or list.
  3. Create a modified endpoint or request using RestClient.setfield
  4. When implementing the List-based method, call perform with the modified request

For example, randomuser.me has a paginated API, and so you could implement support for it with:

@globalconfig RequestConfig("https://randomuser.me/api/")

@jsondef struct User
    gender::String
    name::@NamedTuple{title::String, first::String, last::String}
    email::String
    location::@NamedTuple{country::String, state::String, city::String}
    # ...and lots more
end

@jsondef struct UsersResponse <: ListResponse{User}
    results::Vector{User}
    info::@NamedTuple{seed::String, page::Int, results::Int, version::String}
end

@endpoint struct UsersEndpoint
    users(; page, results, seed) -> "?{page}&{results}&{seed}" -> UsersResponse
    page::Int = 1
    results::Union{Int, Nothing} = 5
    seed::Union{String, Nothing}
end

# Pagination implementation

RestClient.thispagenumber(users::UsersEndpoint) = users.page

RestClient.nextpage(users::UsersEndpoint) =
    RestClient.setfield(users, :page, users.page + 1)

This can then be used like so:

julia> userlist = users(seed = "abc")
List{User} holding 5 items, page 1:
  [...]

julia> nextpage(userlist)
List{User} holding 5 items, page 2:
  [...]
source
RestClient.thispagenumberFunction
thispagenumber(response::List) -> Union{Int, Nothing}

Return the current page number of response, if known.

The generic ::List implementation returns nothing.

source
RestClient.remainingpagesFunction
remainingpages(response::List) -> Union{Int, Nothing}

Return the number of remaining pages after response, if known.

The generic ::List implementation returns nothing.

source

Caching interface

RestClient.cachelifetimeFunction
cachelifetime(req::Request, res::Downloads.Response) -> Union{DateTime, Integer, Nothing}

Determine the expiry time of a cached response, based on the request and response.

Specialisation

To specialise this function for a specific endpoint, define one of the following methods:

cachelifetime(req::Request{kind, <:AbstractEndpoint}, res::Downloads.Response)
cachelifetime([conf::RequestConfig], endpoint::AbstractEndpoint, res::Downloads.Response)
source

Content formatting

RestClient.AbstractFormatType
AbstractFormat

Abstract supertype for response formats.

Typically, you will want to create a singleton subtype and then implement the following methods for it:

  • An interpretresponse method so a response in that format can be parsed/interpreted as a specific type.
  • A writepayload method so data can be sent in that format.
  • A mimetype method so that appropriate headers can be set when sending or receiving data in this format.

See also: RawFormat, JSONFormat.

source
RestClient.JSONFormatType
JSONFormat{backend} <: AbstractFormat

Singleton type for JSON request/response formats. The backend parameter selects the JSON implementation: :json for JSON.jl, :json3 for JSON3.jl.

The zero-argument constructor JSONFormat() picks an available backend at the time of construction.

source
RestClient.dataformatFunction
dataformat([endpoint::AbstractEndpoint], ::Type{T}) -> AbstractFormat

Return the expected format that T is represented by in requests to and responses from endpoint.

Using the default dataformat(::Type) method, the format is RawFormat.

A dataformat(::Type) method is automatically defined when invoking @jsondef.

source
RestClient.interpretresponseFunction
interpretresponse(data::AbstractVector{UInt8}, fmt::AbstractFormat, ::Type{T}) -> value::T

Interpret data as a response of type T according to fmt.

For JSONFormat, the backend-specific methods are implemented in package extensions (JSONExt for JSON.jl, JSON3Ext for JSON3.jl).

source
RestClient.mimetypeFunction
mimetype(::Type{<:AbstractFormat}) -> Union{String, Nothing}

Return the MIME type for the given format, if known.

source