Decorators
To keep our code clean and declarative, we use Custom Decorators for cross-cutting concerns like Caching and Error Handling.
Service Decorators
These are primarily used in the Service layer to manage Redis caching transparently.
@Cacheable
Automatically caches the result of a method in Redis.
- Usage: Read operations (getters).
- Behavior: Checks cache -> Returns if HIT -> Executes method if MISS -> Stores result.
import { Cacheable } from "#decorators/cacheable";
class UserService {
@Cacheable({
namespace: "users",
ttl: 300, // 5 minutes
})
async getUser(id: number) {
return await User.find(id);
}
}
// Cache Key: users:getUser:[1]
Options:
namespace: Grouping key (e.g., 'cameras').ttl: Time To Live in seconds.keyGenerator: Custom function to build the key.condition: Function returning boolean (e.g., don't cache for admins).
@CacheById
A shortcut for @Cacheable when the cache key is a single ID. It generates a cleaner key namespace:id:{value}.
@CacheById({ namespace: 'cameras' })
async getById(id: number) { ... }
// Cache Key: cameras:id:1
@InvalidateCache
Clears stale cache after a write operation.
- Usage: Create, Update, Delete methods.
- Patterns: Supports wildcards (
*).
import { InvalidateCache } from "#decorators/cacheable";
class UserService {
@InvalidateCache({
namespace: "users",
patterns: ["getUser:*", "list:*"],
})
async updateUser(id: number, data: any) {
// ... update logic
}
}
Controller Decorators
These are used in Controllers to standardize request processing.
@HandleError
Wraps the entire controller action in a try/catch block.
- BusinessException: Re-thrown (handled by Global Handler -> 4xx).
- Unknown Error: Caught, Logged with Context (User, URL), and returns 500 "Internal Server Error".
import { HandleError } from "#decorators/handle_error";
class CameraController {
@HandleError("Failed to fetch cameras")
async index({ response }) {
// ...
}
}
@WithPagination
Automatically parses ?limit=10&offset=0 from the query string and injects it into the context.
import { WithPagination } from "#decorators/handle_error";
class CameraController {
@WithPagination
async index({ pagination }: HttpContext & { pagination: any }) {
console.log(pagination.limit); // 10
}
}