santi
santi4mo ago

Is it possible to get the procedure name / id in middleware?

I want to create a caching middleware that in some routes, stores a cache key that contains the route id (example: catalog.product.findMany) and the input (example: { categories: shirts }). It's not clear to me if this is possible.
6 Replies
santi
santi4mo ago
I've noticed that the req.query contains query info, but the problem is that this is batched, so I can't cache, just one of these two requests
No description
santi
santi4mo ago
export default publicProcedure
.use(cacheMiddleware)
.input(GetProductsQuerySchema)
.query(async ({ input, ctx }): Promise<GetProductsQueryResultsLegacy> => {
console.log({ path: __filename })
const results = await ctx.app.get(ProductService).getProducts(input)
return {
...results,
products: results.products.map((p) => p.toLegacy()),
}
})
export default publicProcedure
.use(cacheMiddleware)
.input(GetProductsQuerySchema)
.query(async ({ input, ctx }): Promise<GetProductsQueryResultsLegacy> => {
console.log({ path: __filename })
const results = await ctx.app.get(ProductService).getProducts(input)
return {
...results,
products: results.products.map((p) => p.toLegacy()),
}
})
Trying this to get a filename/routename. I wonder if there's a better way
Alex / KATT 🐱
you can use opts.path in the middleware
export default publicProcedure
.input(GetProductsQuerySchema)
.use(cacheMiddleware)
export default publicProcedure
.input(GetProductsQuerySchema)
.use(cacheMiddleware)
then in the cache middleware you'll have the parsed opts.input which is somewhat normalized
santi
santi4mo ago
import isNil from 'lodash.isnil'
import { RedisService } from '@centrito/api/nest/data/redis.service'
import { t } from '@centrito/api/trpc/server'

const DEFAULT_CACHE_TTL = 60 * 60 * 1 // 1 hour

export const withCaching = t.middleware(async ({ ctx, next, meta, input }) => {
const routeCacheKey = meta?.routeCacheKey
if (routeCacheKey) {
const redis = ctx.app.get(RedisService)
const cacheKey = JSON.stringify({ env: 'dev2', route: routeCacheKey, input })
const cachedData = await redis.get(cacheKey)
if (!isNil(cachedData)) {
return {
data: cachedData,
}
} else {
const result = await next({ ctx })
if (result.ok) {
// No await here, it slows down the request / the response is not needed
redis.setex(cacheKey, DEFAULT_CACHE_TTL, result.data)
}
return result
}
} else {
console.log('Caching skipped as routeCacheKey is not defined')
return next({ ctx })
}
})
import isNil from 'lodash.isnil'
import { RedisService } from '@centrito/api/nest/data/redis.service'
import { t } from '@centrito/api/trpc/server'

const DEFAULT_CACHE_TTL = 60 * 60 * 1 // 1 hour

export const withCaching = t.middleware(async ({ ctx, next, meta, input }) => {
const routeCacheKey = meta?.routeCacheKey
if (routeCacheKey) {
const redis = ctx.app.get(RedisService)
const cacheKey = JSON.stringify({ env: 'dev2', route: routeCacheKey, input })
const cachedData = await redis.get(cacheKey)
if (!isNil(cachedData)) {
return {
data: cachedData,
}
} else {
const result = await next({ ctx })
if (result.ok) {
// No await here, it slows down the request / the response is not needed
redis.setex(cacheKey, DEFAULT_CACHE_TTL, result.data)
}
return result
}
} else {
console.log('Caching skipped as routeCacheKey is not defined')
return next({ ctx })
}
})
@Alex / KATT 🐱, thanks for taking the time. I'm struggling to set up this cache middleware What should the return be when the results ARE cached? Right now I wrote the following:
return {
data: cachedData,
}
return {
data: cachedData,
}
But this is surely wrong
santi
santi4mo ago
https://github.com/trpc/trpc/issues/4066 <-- seems related to this feature request
GitHub
feat: Allow middlewares to intercept and override procedure result ...
Describe the feature you'd like to request Hi 👋 As I mentioned here, I'm using createCaller with Next.js /app to perform server-side queries. Because Next.js might expect to run multiple qu...
Sandvich
Sandvich4mo ago
Until that feature gets implemented you might just have to do:
return {
ctx: {
cache: cachedData,
}
}
return {
ctx: {
cache: cachedData,
}
}
Then in your procedures
...
.query(({ ctx }) => {
if (ctx.cache) return ctx.cache;

...
})
...
.query(({ ctx }) => {
if (ctx.cache) return ctx.cache;

...
})
It's not ideal but it should work