The bill is the problem, not the milliseconds
Every repeated call to a metered API is money you have already spent, spent again. With LLM providers this adds up quickly, because the same prompt, the same document and the same enrichment lookup get charged every time they go out.
The standard fix is Redis. That means another managed service, another instance to size, another bill and another dependency to keep alive, all for what is essentially a lookup table sitting in front of a request.
Codehooks puts the key-value store and the request handler in the same deploy, so caching, rate limiting and key rotation live where the traffic already passes.
What the usual fix actually costs you
| Redis or Upstash, plus your proxy | Codehooks |
|---|---|
| A cache service, billed separately | Key-value store included |
| Somewhere to run the proxy itself | Handler and store in one deploy |
| Instance sizing and eviction tuning | Nothing to size |
| API keys held in a third place | Keys and rotation beside the handler |
| Two dashboards and two failure modes | One deploy |
Cache-aside in front of a metered API
The whole thing. Notice that no second service appears anywhere in the file: no cache to connect to, no instance to size, no separate set of credentials.
import { app, Datastore } from 'codehooks-js';
import crypto from 'crypto';
app.post('/complete', async (req, res) => {
const db = await Datastore.open();
// Hash everything that should change the answer, not just the prompt
const key = 'llm:' + crypto
.createHash('sha256')
.update(JSON.stringify({
model: req.body.model,
prompt: req.body.prompt
}))
.digest('hex');
// Already paid for this one
const hit = await db.get(key);
if (hit) return res.json(JSON.parse(hit));
const answer = await callLlm(req.body.prompt);
await db.set(key, JSON.stringify(answer), { ttl: 86400000 });
res.json(answer);
});
export default app.init();
Questions worth asking first
Including the one where a dedicated cache service is the better answer
Why not just use Upstash? It is cheap.
Is it fast enough to sit in the request path?
getMany or parallel reads instead.My provider already offers prompt caching. Isn't that the same thing?
What about cache invalidation?
{ ttl: 86400000 } for a day). An explicit purge removes a key when you know the underlying data changed. And a scheduled job can refresh entries before they expire, which suits data that is expensive to fetch and read constantly.Can I cache things other than LLM responses?
Where is the data stored?
Stop paying for the same answer twice
A key-value store and a request handler in one deploy, so the cache lives where the traffic already passes. The rest of the platform is on the Codehooks homepage.
Nothing to size · Data stored in the EU