Skip to main content

Caching

Efesto has a small, built-in response cache backed by Redis, on the Express stack. You enable Redis once in the config, then declare a cache key per endpoint. On a cache hit the endpoint method is skipped and the stored JSON is returned directly.

Express only

The Redis cache described here is implemented by the Express adapter. The Bun/Elysia adapter does not add caching, use Elysia's native caching plugins/patterns there.

1. Enable Redis

Add a redis block to the Efesto config. The cache stays off until this is present:

efesto({
// ...
options: {
config: {
redis: {
host: "127.0.0.1", // Redis host
port: 6379, // Redis port
defaultExpiresInSeconds: 600, // fallback TTL when an endpoint doesn't set one
},
},
},
});

2. Cache an endpoint

Declare cache inside the method's _<method>Swagger object. On a hit, Efesto returns the cached value and never runs your method:

import { BaseApiService, SwaggerOptions } from "efesto";

class Users extends BaseApiService {
constructor() {
super(__filename);
}

_getSwagger: SwaggerOptions = {
operationId: "getUsers",
cache: {
key: "`users`", // see the note on backticks below
expiresInSeconds: 60, // optional; overrides defaultExpiresInSeconds
},
};

_get(req, res) {
return res.json(listUsers());
}
}
export default Users;

Only successful responses are cached: Efesto stores the body when the status code is in the 200299 range. The TTL resolves as expiresInSeconds → config.redis.defaultExpiresInSeconds → 60.

The key is an evaluated expression

This is the one detail to get right. The key string is evaluated as JavaScript at request time, with access to the req object. That is why the value is wrapped in backticks inside the string, you are writing a template literal as a string:

cache: {
// a string containing a backtick template literal
key: "`user-${req.user.id}-profile`",
}

For a request where req.user.id is 622b8386..., the resulting Redis key is user-622b8386...-profile. A static key like "`users`" still needs the inner backticks.

Security

Because the key is passed through eval(), treat it as code, not data. Build keys only from values you control (req.user.id, req.params), never by interpolating unsanitized client input into the expression in a way that could inject code.

3. Invalidate the cache

A mutating endpoint clears cached keys with purgeKey. It accepts one key or an array, each an evaluated expression like key:

_postSwagger: SwaggerOptions = {
operationId: "createUser",
purgeKey: ["`users`"], // delete this key (or keys) when this endpoint runs
};

_post(req, res) {
const user = createUser(req.body);
return res.json(user); // cached "users" entry is purged on the way through
}
Property name

The property is purgeKey (singular purge does not work). purgeKey values are evaluated and may use Redis key patterns supported by KEYS, e.g. "`user-*`" to clear a family of keys.

Putting it together

A typical read/write pair: cache the listing, purge it on create.

class Users extends BaseApiService {
constructor() { super(__filename); }

_getSwagger: SwaggerOptions = {
operationId: "getUsers",
cache: { key: "`users`", expiresInSeconds: 120 },
};
_get(req, res) { return res.json(listUsers()); }

_postSwagger: SwaggerOptions = {
operationId: "createUser",
purgeKey: ["`users`"],
};
_post(req, res) { return res.json(createUser(req.body)); }
}

Scope and limits

Efesto's cache is deliberately minimal: a per-endpoint key, a TTL, and key invalidation. It does not provide write-through layers, background refresh, cache warming, statistics, or health endpoints. If you need those, talk to Redis directly in your handlers, or use the client of your choice alongside Efesto.