Skip to content

Cache

Jazzy includes a lightweight, built-in memory cache system. Caching frequently accessed data or expensive operations can drastically improve your application’s performance.

The cache is globally shared across requests but can be conveniently accessed directly from the Context object via ctx.cache.

You can store data in the cache using the put method. The third parameter is the Time-To-Live (TTL) in seconds. If omitted, it defaults to 3600 seconds (1 hour).

# Put a string. By default, it stays for 1 hour (3600 seconds)
ctx.cache.put("token", "xyz123")
# Put a string that only lasts for 15 seconds
ctx.cache.put("sms_code", "1234", 15)

The cache is heavily optimized to store and retrieve Nim JsonNode types, which makes it perfect for API responses.

import std/json
let data = %*{
"name": "Jazzy",
"role": "admin"
}
# Cache the JsonNode (defaults to 1 hour)
ctx.cache.put("user_data", data)

You can retrieve standard string values using the get method.

let value = ctx.cache.get("token")
# If the key doesn't exist (or expired), it returns an empty string "".
# You can provide a custom fallback default value:
let fallback = ctx.cache.get("missing_key", "default_value")

To safely retrieve JSON data, use the getJson method. If the key doesn’t exist, has expired, or contains invalid JSON, it returns an empty node that can be checked with .isNull().

let retrieved = ctx.cache.getJson("user_data")
if not retrieved.isNull():
echo "Welcome back, ", retrieved.getString("name")

You can also provide a default JSON node to be returned if the cache misses:

let defJson = %*{"default": true}
let missingDef = ctx.cache.getJson("missing_json", defJson)

To quickly check if a key exists and hasn’t expired, use the has method.

if ctx.cache.has("token"):
echo "Cache hit!"

You can manually remove items from the cache using delete.

ctx.cache.delete("to_delete")

Jazzy’s cache handles expiration lazily by default. This means if you .get() or .has() a key whose TTL has already passed, the cache will automatically delete it and return the default value/false.

However, if you want to aggressively clean up all expired memory in the background, you can manually call the prune method.

ctx.cache.prune()

Here is a full example of a controller utilizing the cache to avoid unnecessary database queries:

import jazzy
proc getProfile*(ctx: Context) {.async.} =
let userId = ctx.param("id")
let cacheKey = "user_profile_" & userId
# 1. Check if the profile is in the cache
if ctx.cache.has(cacheKey):
let cachedData = ctx.cache.getJson(cacheKey)
ctx.json(%*{
"source": "cache",
"data": cachedData
})
return
# 2. Simulate an expensive database query
# In a real app, you would query your DB here
let dbData = %*{
"id": userId,
"username": "jazzy_dev",
"rank": "diamond"
}
# 3. Store the result in the cache for 15 minutes (900 seconds)
ctx.cache.put(cacheKey, dbData, 900)
# 4. Return the freshly fetched data
ctx.json(%*{
"source": "database",
"data": dbData
})