Last active
October 4, 2024 18:23
-
-
Save laser/717b6851b9e728f7376be2ab2c58d5f8 to your computer and use it in GitHub Desktop.
Easy TypeScript Decorator for TTL Cache (using isaacs/ttlcache)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ServerInferRequest, ServerInferResponses, initContract } from '@ts-rest/core'; | |
import { createExpressEndpoints, initServer } from '@ts-rest/express'; | |
import express from 'express'; | |
import { z } from 'zod'; | |
import { CacheMethod } from '~/util/cache-method'; | |
export const EchoSchema = initContract().router({ | |
echo: { | |
body: z.object({ | |
call: z.string(), | |
}), | |
method: 'POST', | |
path: '/echo', | |
responses: { | |
200: z.object({ | |
response: z.string(), | |
}), | |
}, | |
}, | |
}); | |
class EchoController { | |
private logger: (msg: string) => void; | |
constructor(logger: (msg: string) => void) { | |
this.logger = logger; | |
} | |
@CacheMethod(5_000, (req) => JSON.stringify(req.body), (res) => res.status === 200) | |
public async echo( | |
req: ServerInferRequest<typeof EchoSchema>['echo'], | |
): Promise<ServerInferResponses<typeof EchoSchema>['echo']> { | |
this.logger(`echoing: ${req.body.call}`); | |
return { | |
body: { | |
response: `you said: ${req.body.call}`, | |
}, | |
status: 200, | |
}; | |
} | |
} | |
const app = express(); | |
app.use(express.urlencoded({ extended: false })); | |
app.use(express.json()); | |
const logger = (msg: string) => console.log(msg); | |
const controller = new EchoController(logger); | |
const router = initServer().router(EchoSchema, { echo: controller.echo.bind(controller) }); | |
createExpressEndpoints(EchoSchema, router, app); | |
const host = '127.0.0.1'; | |
const port = 8888; | |
console.log(`starting server: host=${host}, port=${port}`); | |
app.listen(port, host); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/no-explicit-any */ | |
import TTLCache from '@isaacs/ttlcache'; | |
export function CacheMethod<T, U, K>(ttlMs: number, computeKey: (input: T) => K, shouldCache: (output: U) => boolean) { | |
if (ttlMs <= 0) { | |
return function ( | |
_target: unknown, | |
_propertyKey: string, | |
_descriptor: TypedPropertyDescriptor<(input: T) => Promise<U>>, | |
) { | |
// noop | |
}; | |
} | |
return function (target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<(input: T) => Promise<U>>) { | |
const original = descriptor.value!; | |
descriptor.value = function (input: T): Promise<U> { | |
const that: any = this; | |
if (!that.__cache) { | |
that.__cache = {}; | |
} | |
if (!that.__cache[`${target.constructor.name}__${propertyKey}`]) { | |
that.__cache[`${target.constructor.name}__${propertyKey}`] = new TTLCache<K, U>({ ttl: ttlMs }); | |
} | |
const key = computeKey(input); | |
const hit = that.__cache[`${target.constructor.name}__${propertyKey}`].get(key); | |
if (hit) { | |
return Promise.resolve(hit); | |
} | |
return Promise.resolve(original.call(this, input)).then((output: U) => { | |
if (shouldCache(output)) { | |
that.__cache[`${target.constructor.name}__${propertyKey}`].set(key, output); | |
} | |
return output; | |
}); | |
}; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment