Skip to main content

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
}
}