$fetch
Make HTTP requests with automatic JSON parsing and disk caching.
Basic Usage
The simplest form - just a URL:
val data = $fetch("https://api.example.com/data") HTTP Methods
GET
val shows = $fetch.get(
url = "https://api.example.com/shows",
headers = { Authorization: "Bearer $token" },
params = { page: "1", limit: "20" }
) POST
val response = $fetch.post(
url = "https://api.example.com/login",
body = { email: email, password: password }
) PUT
$fetch.put(
url = "https://api.example.com/profile",
body = { name: name, bio: bio }
) DELETE
$fetch.delete(
url = "https://api.example.com/posts/$id",
headers = { Authorization: "Bearer $token" }
) Disk Caching
A GET with cache is kept on disk for that long, keyed by method, full URL and the Authorization header — one user's cached response is never served to another.
Only GET is cached; clearing is all or nothing.
// Cache for 1 day
val show = $fetch.get(
url = "https://api.example.com/show/$id",
cache = "1d"
)
// Cache forever (static data)
val countries = $fetch.get(
url = "https://api.example.com/countries",
cache = "forever"
) Supported TTL Formats
"30s"- 30 seconds"5m"- 5 minutes"1h"- 1 hour"1d"- 1 day"7d"- 7 days"forever"- Never expires
Clear Cache
$fetch.clearCache() This is a different cache from a route's. The route cache is in memory, per route, and survives rotation but not a restart. The fetch cache is on disk, per URL, and survives a restart — the place for data that is fine slightly stale, or truly static.
See it happen
The method and the cache option each change what gets generated. Flip between these to see it.
suspend fun loadShow(id: String): String {
return $fetch("https://api.example.com/show/${id}")
}
<Text>Show</Text>The plain form is a single call to Ktor’s httpClient.get, with a private HttpClient generated once at file scope.
In a Screen Loader
Use $fetch in data loaders for automatic loading states:
// +screen.load.wh
suspend fun load(): Show {
val id = $screen.params.id
return $fetch("https://api.example.com/shows/$id")
}
// +screen.wh
<Column>
<Text>{$data.name}</Text>
<Text>{$data.description}</Text>
</Column> Error Handling
Use try-catch for error handling:
var error: String? = $state(null)
var data: Show? = $state(null)
suspend fun loadData() {
try {
data = $fetch("https://api.example.com/show/$id")
} catch (e: Exception) {
error = e.message
}
}
$onMount { loadData() }
@if (error != null) {
<Text color="#ef4444">Error: {error}</Text>
} else if (data != null) {
<Text>{data.name}</Text>
} $fetch automatically parses JSON responses and handles serialization for request bodies.
Type Safety
Define data classes for type-safe responses:
data class Show(
val id: String,
val name: String,
val description: String,
val posterUrl: String
)
val show: Show = $fetch("https://api.example.com/shows/$id") See Also
- $data - Data loading patterns
- $onMount / $onDispose - Lifecycle hooks
- $dispatch / $periodic - Background requests