glyde/rest
A Discord call as data. rest.request builds a gleam_http
Request(Wire); rest.route gives the limiter its scheduling identity.
let config = rest.config(rest.bot(token))
let call =
rest.post(
[seg.lit("channels"), seg.channel(channel_id), seg.lit("messages")],
body.json([#("content", json.string("pong"))]),
rest.Decoded(message.decoder()),
)
// Send it with any HTTP client, then hand the answer back to the call.
let posted = rest.response(call, status, headers, bytes)
Types
One Discord call, described and inert. Parameterised by what the endpoint
returns. The bot token lives in Config and never here; a webhook or
interaction credential is part of the path, and auth says so.
pub opaque type Call(a)
pub type Config {
Config(
token: Token,
host: String,
api_version: Int,
user_agent: String,
boundary: body.Boundary,
)
}
Constructors
-
Config( token: Token, host: String, api_version: Int, user_agent: String, boundary: body.Boundary, )Arguments
- host
-
Overridable for a proxy or a recording double.
pub type Expect(a) {
Decoded(decode.Decoder(a))
NoContent(a)
}
Constructors
-
Decoded(decode.Decoder(a)) -
NoContent(a)A 204 endpoint.
ais what to return; usuallyNil.
glyde/rest/error has the questions worth asking of one: retry_advice,
counts_as_invalid_request, is_token_fatal.
pub type Failure =
error.ApiError
Values
pub fn age_bucket(call: Call(a), created_at_ms: Int) -> Call(a)
Bucket a message deletion by the target’s age. Discord splits these and says so in no header (discord-api-docs#1295); the limiter picks the band.
pub const api_version: Int
An unversioned path routes to the deprecated v6, so this is never optional.
pub fn attach(call: Call(a), file: body.File) -> Call(a)
Add a file, turning the body multipart. Appends, because a part is named
files[n] from its position and prepending would renumber the rest.
The other two bodies come back unchanged, and neither can lose a file that
way: body.JsonArray is only ever a bulk replace, which takes no uploads,
and a body.Finished payload already names the parts it carries, so an
extra one would have no attachments entry to be matched against.
pub const base_url: String
For a request built by hand rather than from a Call. Gleam cannot
concatenate constants, so a test pins it against host and api_version.
pub fn bearer(token: String) -> Token
An OAuth2 access token, sent as Authorization: Bearer <token>.
pub const boundary: body.Boundary
Discord requires the boundary to be absent from the parts, not
unpredictable, and body.encode grows this one when a part contains it.
pub fn path_authenticated(call: Call(a)) -> Call(a)
For the webhook and interaction routes: send no Authorization header,
whatever the Config holds. A 401 then means that path credential.
pub fn query(call: Call(a), params: List(query.Param)) -> Call(a)
Add query parameters. They accumulate rather than replace, so a repeated
key produces ?id=1&id=2, which is how Discord reads one. query.to_string
spells them out, percent-encoded, when the request is built.
Only glyde/rest/query can build a Param, so absence has no spelling
here either: there is nothing to hand in for a parameter you are omitting.
pub fn reason(call: Call(a), why: String) -> Call(a)
X-Audit-Log-Reason, percent-encoded: a line break in a header value would
split the request. Discord’s 512 character limit is not enforced here.
pub fn request(
config: Config,
call: Call(a),
) -> request.Request(body.Wire)
Always HTTPS, so no config field can send a token in the clear. A plain
HTTP double is one request.set_scheme away.
pub fn response(
call: Call(a),
status status: Int,
headers headers: List(#(String, String)),
body body: BitArray,
) -> Result(a, error.ApiError)
Interpret a response with the decoder the Call carries. Success is the
whole 2xx range: 201 and 204 are everyday Discord answers.
pub fn route(call: Call(a)) -> route.Route
The scheduling identity. Available without building a Request.
pub fn split_bucket(call: Call(a), name: String) -> Call(a)
Split this call into its own rate-limit bucket, for a Discord sublimit.
pub fn unauthenticated() -> Token
No Authorization header. The webhook and interaction routes drop theirs
per call, so this is for a route glyde does not wrap.
pub const user_agent: String
Discord’s required shape; without it Cloudflare answers a misleading
“invalid form body”. Keep the version in step with gleam.toml.