Requests
request Http , response caching, safe build/parse hooks.
request Http {} declares a whole .thr
component (extends="Task" required) as a single HTTP endpoint — one
component per endpoint.
Declaring an endpoint
Static config (method/url/headers/query/body)
covers the fixed shape; anything that varies per call flows through the optional
buildRequest/parseResponse/parseError hooks.
<script>
request Http {
method: "GET",
url: "https://jsonplaceholder.typicode.com/posts"
}
private function buildRequest(requestData: object): object {
return { query: { "userId": requestData.userId } }
}
private function parseResponse(response: object): object {
return { count: response.data.Count() }
}
private function parseError(response: object): object {
return { message: "HTTP " + response.httpStatusCode.ToStr() }
}
</script>
<component extends="Task">
</component>Calling it
When a component declares buildRequest, it also gets a generated
prepareRequest(requestData) — call it before taskManager.run(task), on the calling thread, so nothing inside
buildRequest ever risks a cross-thread rendezvous.
task = CreateObject("roSGNode", "GetPosts")
task.callFunc("prepareRequest", { userId: 1 }) ' resolves buildRequest BEFORE the Task thread starts
taskManager.run(task)
taskManager.onResult(task, onPostsLoaded, onPostsFailed)
See Task manager for onResult
and the app-wide onRequestSent/onResponseReceived
interceptors. Both a promise-style (onResult) and an old-style
(observeFieldScoped("result"/"error", ...)) consumption style are fully
supported side by side.
Response caching — on by default
GET-only, backed by Roku's own cachefs:/. A request with no
cache key at all still caches, purely following the server's own
Cache-Control header — cache is only an
override.
request Http {
method: "GET",
url: "https://api.example.com/catalog"
' caches automatically, following the server's own Cache-Control — no "cache" key needed
}
request Http {
method: "GET",
url: "https://api.example.com/live-price",
cache: false ' force caching OFF — always a real network request
}
request Http {
method: "GET",
url: "https://api.example.com/legacy-endpoint",
cache: { ttlSeconds: 300 } ' force this exact lifetime, ignoring Cache-Control
}Crash safety
Every hook — buildRequest, parseResponse,
parseError — is wrapped in a try/catch by
the compiler. A buggy hook degrades to a synthesized fallback error (or the static base config, for
buildRequest) instead of crashing the Task — surfaced via
resolvedOptions.buildSucceeded/rawResponse.parseSucceeded for
telemetry.
Reference implementation — apps/requests-demo
Every mechanism on this page has a router-mounted, scaled
chapter in apps/requests-demo — 4 chapters
(/declare-call through /parse-safety),
reachable with REWIND/FAST-FORWARD once compiled and sideloaded. Each chapter shows a default,
no-customization example alongside a deliberately different, customized one — e.g.
DeclareCallDemo.thr's default button (promise-style
onResult only) next to its customized button (the same
request consumed via onResult AND the old-style
observeFieldScoped("result"/"error", ...), side by side).
This is the live, compiling reference for the whole page — see
findings/requests-demo-app.md for what each chapter covers and
findings/demo-app-conventions.md for the app-structure convention it follows.
⚠️ Not (yet) supported
- ○ Only
request Http {}is supported — any otherKindis a compile error. - ○ No retry logic, no cancellation, no request timeout — a hung transfer blocks only its own Task thread.
- ○ No ETag/conditional-GET revalidation, no
Expiresheader parsing — onlyCache-Control: max-age. - ○
cacheis not overridable per call viabuildRequest— it's a property of the endpoint, not the call site. - ○ Bareword object keys inside a hook body's return value get case-folded by BrightScript at runtime — always quote a
headers/querykey that must match an API case-exactly.
Exact grammar: GRAMMAR.md. Full feature status: docs/features.md.