diff --git a/apps/blakus-api/src/app/plugins/caching.ts b/apps/blakus-api/src/app/plugins/caching.ts new file mode 100644 index 0000000..dec52ec --- /dev/null +++ b/apps/blakus-api/src/app/plugins/caching.ts @@ -0,0 +1,65 @@ +import { FastifyInstance } from 'fastify'; +import fp from 'fastify-plugin'; +import Caching from '@fastify/caching'; + +declare module 'fastify' { + interface FastifyContextConfig { + serverCache?: { + key?: string; + ttl: number; + }; + } +} + +export default fp(async function (fastify: FastifyInstance) { + + fastify.register(Caching,{ + expiresIn: 10000, + privacy: Caching.privacy.PUBLIC, + // TODO: default is a mem cache, should use redis cache for prod, for example + }) + .addHook('onRoute', (routeOptions) => { + const { config } = routeOptions; + + if (config?.serverCache) { + + const { key, ttl = 10000 } = config.serverCache; + + // set or add our handler to the route preHandler hook + Object.entries({ + preHandler: (request, reply, done) => { + fastify.cache.get(key || encodeURIComponent(request.raw.url), (err, cached) => { + if (err) { + // TODO: handle error + done(); + return; + } + if (cached?.item) { + console.log('from cache'); + reply.send(cached.item); + } else { + console.log('getting fresh data'); + } + + done(); + }); + + return reply; + }, + onSend: (request, _reply, payload, done) => { + fastify.cache.set(key || encodeURIComponent(request.raw.url), payload, ttl, () => {done();}); + }, + }).forEach(([hook, handler]) => { + if (!routeOptions[hook]) { + routeOptions[hook] = [handler] + return + } + if (Array.isArray(routeOptions[hook])) { + routeOptions[hook].push(handler) + return + } + routeOptions[hook] = [routeOptions[hook], handler] + }) + } + }) +}); diff --git a/apps/blakus-api/src/app/routes/blakus.ts b/apps/blakus-api/src/app/routes/blakus.ts index f2bc6f5..fa699d3 100644 --- a/apps/blakus-api/src/app/routes/blakus.ts +++ b/apps/blakus-api/src/app/routes/blakus.ts @@ -22,15 +22,16 @@ export default async function (fastify: FastifyInstance) { } } } + }, + config: { + serverCache: { ttl: 10000 } } - }, async function ({query}) { + }, async function (request, reply) { + const {lat, lon} = request.query; const elements = await overpassService.getAmenitiesAroundLocation({ - latlon: { - lat: query.lat, - lon: query.lon, - }, + latlon: { lat, lon }, amenities: ['restaurant', 'cafe', 'bar', 'pub', 'biergarten', 'fast_food', 'food_court', 'ice_cream'] }); - return elements; + reply.send(elements); }); } diff --git a/apps/blakus-api/src/app/routes/root.ts b/apps/blakus-api/src/app/routes/root.ts index e361629..6b7369d 100644 --- a/apps/blakus-api/src/app/routes/root.ts +++ b/apps/blakus-api/src/app/routes/root.ts @@ -2,8 +2,12 @@ import { FastifyInstance } from 'fastify'; import db from '../../db'; export default async function (fastify: FastifyInstance) { - const users = await db.select('*').from('users'); - fastify.get('/', async function () { - return { users }; + fastify.get('/', { + config: { + serverCache: { ttl: 10000 } + } + }, async function (req, reply) { + const users = await db.select('*').from('users'); + reply.send({ users }); }); } diff --git a/package-lock.json b/package-lock.json index 52d1cf5..d394364 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "MIT", "dependencies": { "@fastify/autoload": "~6.0.3", + "@fastify/caching": "^9.0.3", + "@fastify/etag": "^6.0.3", "@fastify/sensible": "~6.0.2", "@nativescript/core": "~8.8.0", "@sinclair/typebox": "^0.34.28", @@ -2686,12 +2688,52 @@ ], "license": "MIT" }, + "node_modules/@fastify/caching": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@fastify/caching/-/caching-9.0.3.tgz", + "integrity": "sha512-5K/2shYpvWHWiSAs59SaCVBoFhHEF8Yz4TTiXZf8YWVDcxuIxw0Adn5eDQ7s132s7vwURNOnCKHBjUQSOI+PLA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "abstract-cache": "^1.0.1", + "fastify-plugin": "^5.0.0", + "uid-safe": "^2.1.5" + } + }, "node_modules/@fastify/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.0.0.tgz", "integrity": "sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==", "license": "MIT" }, + "node_modules/@fastify/etag": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@fastify/etag/-/etag-6.0.3.tgz", + "integrity": "sha512-pMXohCA6Bk9JBNLl/KhHkSCGN7PCbgwVqHydwMsd1sVKtV8YNuzRW7lEe1VYap/MTNOn/Q8OfcR667lyrNbT/A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0" + } + }, "node_modules/@fastify/fast-json-stringify-compiler": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.2.tgz", @@ -5368,6 +5410,26 @@ "license": "ISC", "optional": true }, + "node_modules/abstract-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/abstract-cache/-/abstract-cache-1.0.1.tgz", + "integrity": "sha512-EfUeMhRUbG5bVVbrSY/ogLlFXoyfMAPxMlSP7wrEqH53d+59r2foVy9a5KjmprLKFLOfPQCNKEfpBN/nQ76chw==", + "license": "MIT", + "dependencies": { + "clone": "^2.1.1", + "lru_map": "^0.3.3", + "merge-options": "^1.0.0" + } + }, + "node_modules/abstract-cache/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", @@ -9430,6 +9492,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -10694,6 +10765,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -10822,6 +10899,18 @@ "node": ">= 4.0.0" } }, + "node_modules/merge-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", + "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^1.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/merge-source-map": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", @@ -12327,6 +12416,15 @@ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -14096,6 +14194,18 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", diff --git a/package.json b/package.json index 4cb39ef..f7f9417 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ }, "dependencies": { "@fastify/autoload": "~6.0.3", + "@fastify/caching": "^9.0.3", + "@fastify/etag": "^6.0.3", "@fastify/sensible": "~6.0.2", "@nativescript/core": "~8.8.0", "@sinclair/typebox": "^0.34.28",