Helpers & Cache
Context Reference - Helpers & Cache Utility functions for common tasks and distributed caching operations. Helpers Utility functions for common tasks. All helper functions require await . JWT Token Generation const token = await $ctx.$helpers.$jwt(payload, expiration); // Example
Context Reference - Helpers & Cache
Utility functions for common tasks and distributed caching operations.
Helpers
Utility functions for common tasks. All helper functions require await.
JWT Token Generation
const token = await $ctx.$helpers.$jwt(payload, expiration);
// Example
const token = await $ctx.$helpers.$jwt(
{ userId: 123, email: '[email protected]' },
'7d' // Expires in 7 days
);
Expiration formats:
- '15m' - 15 minutes
- '1h' - 1 hour
- '1d' - 1 day
- '7d' - 7 days
- '30d' - 30 days
Password Hashing
// Hash password
const hash = await $ctx.$helpers.$bcrypt.hash(plainPassword);
// Verify password
const isValid = await $ctx.$helpers.$bcrypt.compare(plainPassword, hashedPassword);
// Example
const hashedPassword = await $ctx.$helpers.$bcrypt.hash('myPassword123');
const isValid = await $ctx.$helpers.$bcrypt.compare('myPassword123', hashedPassword);
Auto Slug Generation
Generate URL-friendly slugs from text.
const slug = $ctx.$helpers.autoSlug(text);
// Example
const slug = $ctx.$helpers.autoSlug('My Product Name');
// Result: 'my-product-name'
Crypto Helpers
Use $ctx.$helpers.$crypto for bounded cryptographic helpers inside hooks, handlers, flows, and websocket scripts.
const id = $ctx.$helpers.$crypto.randomUUID();
const token = $ctx.$helpers.$crypto.randomBytes(32, 'base64url');
const digest = $ctx.$helpers.$crypto.sha256('payload');
const signature = $ctx.$helpers.$crypto.hmacSha256('payload', 'shared-secret');
Supported encodings for randomBytes, sha256, and hmacSha256 are hex, base64, and base64url. randomBytes is capped to 4096 bytes.
Generate SSH keys with the same crypto helper:
const keyPair = await $ctx.$helpers.$crypto.generateSshKeyPair('[email protected]');
// keyPair.publicKey is OpenSSH format.
// keyPair.privateKey is RSA PKCS#1 PEM.
Do not use legacy $ctx.$helpers.$ssh or manual encryption helpers in new scripts. Database values that need encryption at rest should be stored in columns marked isEncrypted=true; scripts read and write plaintext values.
Sleep Helper
Pause a dynamic script for a bounded duration. The runtime clamps the delay between 0 and 30000 milliseconds.
await $ctx.$helpers.$sleep(1000);
Storage Helpers
Upload files to storage.
const fileResult = await $ctx.$storage.$upload({
originalname: 'image.jpg',
filename: 'custom-filename.jpg',
mimetype: 'image/jpeg',
buffer: fileBuffer,
size: 1024000,
folder: 123, // Optional: folder ID
storageConfig: 1, // Optional: storage config ID
title: 'My Image', // Optional
description: 'Image description' // Optional
});
File Update Helper
Update existing files.
await $ctx.$storage.$update(fileId, {
buffer: newFileBuffer,
originalname: 'new-name.jpg',
mimetype: 'image/jpeg',
folder: 456,
title: 'Updated Title'
});
File Delete Helper
Delete files.
await $ctx.$storage.$delete(fileId);
Register an object that already exists in the configured storage backend without uploading bytes:
await $ctx.$storage.$registerFile({
filename: 'backup.sql.gz',
mimetype: 'application/gzip',
location: 'backups/project-1/backup.sql.gz',
size: 1024,
storageConfig: 1,
});
Rate Limiting
Protect your API routes with flexible rate limiting using Redis sliding window algorithm.
const result = await $ctx.$helpers.$rateLimit.byIp({
maxRequests: 100,
perSeconds: 60
});
if (!result.allowed) {
$ctx.$throw['429'](`Rate limit exceeded. Try again in ${result.retryAfter}s`);
}
Rate Limit Templates
| Method | Key Format | Description |
|---|---|---|
byIp(options) |
ip:{ip}:{route} |
Rate limit by client IP per route |
byUser(options) |
user:{userId}:{route} |
Rate limit by authenticated user per route |
byRoute(options) |
route:{route} |
Rate limit globally per route (all users/IPs share limit) |
byIpGlobal(options) |
ip:{ip} |
Rate limit by IP across all routes |
byUserGlobal(options) |
user:{userId} |
Rate limit by user across all routes |
check(key, options) |
Custom key | Rate limit with custom key |
reset(key) |
- | Reset rate limit for a key |
status(key, options) |
- | Check rate limit status without incrementing |
Options
{
maxRequests: 100, // Maximum requests allowed in the window
perSeconds: 60 // Time window in seconds
}
Result Object
{
allowed: boolean, // Whether the request is allowed
remaining: number, // Remaining requests in window
resetAt: number, // Unix timestamp when window resets
retryAfter: number, // Seconds to wait before retry (0 if allowed)
limit: number, // The max requests limit
window: number // The window in seconds
}
Examples
Rate Limit Login Attempts by IP:
// In preHook for POST /auth/login
const result = await $ctx.$helpers.$rateLimit.byIp({
maxRequests: 5,
perSeconds: 60
});
if (!result.allowed) {
$ctx.$throw['429'](`Too many login attempts. Try again in ${result.retryAfter}s`);
}
Rate Limit API by User:
// In preHook for API routes
const result = await $ctx.$helpers.$rateLimit.byUser({
maxRequests: 1000,
perSeconds: 3600 // 1 hour
});
if (!result.allowed) {
$ctx.$throw['429'](`API rate limit exceeded. Try again in ${result.retryAfter}s`);
}
Skip Rate Limit for Admins:
if (!$ctx.$user?.isRootAdmin) {
const result = await $ctx.$helpers.$rateLimit.byIp({
maxRequests: 100,
perSeconds: 60
});
if (!result.allowed) {
$ctx.$throw['429']('Rate limit exceeded');
}
}
Custom Key for Specific Resource:
const resourceId = $ctx.$params.id;
const result = await $ctx.$helpers.$rateLimit.check(
`resource:${resourceId}:${$ctx.$user?.id || $ctx.$req.ip}`,
{ maxRequests: 10, perSeconds: 60 }
);
if (!result.allowed) {
$ctx.$throw['429']('Too many requests to this resource');
}
Check Status Without Incrementing:
// Check if rate limited without counting the request
const status = await $ctx.$helpers.$rateLimit.status(
`ip:${$ctx.$req.ip}:/api/expensive`,
{ maxRequests: 5, perSeconds: 3600 }
);
if (!status.allowed) {
$ctx.$throw['429']('Daily limit reached');
}
Reset Rate Limit After Successful Action:
// In postHook after successful action
await $ctx.$helpers.$rateLimit.reset(`ip:${$ctx.$req.ip}:/auth/forgot-password`);
Cache
Distributed user-cache and locking operations. All cache functions require await.
$ctx.$cache stores application data created by handlers, hooks, flows, websocket scripts, and the @CACHE macro. Use logical keys such as user:123; do not include NODE_NAME, user_cache:, or any Redis namespace prefix. When Redis user cache is enabled, Enfyra stores those values under the current app namespace as NODE_NAME:user_cache:*.
The user cache has a soft allocation controlled by REDIS_USER_CACHE_LIMIT_MB (default 30). If the allocation is exceeded, Enfyra evicts least-recently-used user-cache keys only. System Redis keys such as runtime cache snapshots, BullMQ queues, Socket.IO, runtime telemetry, and locks are not counted or evicted by this quota.
Get Cached Value
const cachedValue = await $ctx.$cache.get(key);
// Example
const user = await $ctx.$cache.get('user:123');
Set Cached Value
// Set with TTL (time-to-live in milliseconds)
await $ctx.$cache.set(key, value, ttlMs);
// Example - cache for 5 minutes
await $ctx.$cache.set('user:123', userData, 300000);
// Set without expiration
await $ctx.$cache.setNoExpire(key, value);
Prefer set(key, value, ttlMs) with a TTL for operational data. setNoExpire keeps a value persistent from a TTL perspective, but it can still be evicted if the user-cache allocation is exceeded.
Distributed Locking
Acquire and release locks for critical operations.
// Acquire lock
const lockAcquired = await $ctx.$cache.acquire(key, value, ttlMs);
// Release lock
const released = await $ctx.$cache.release(key, value);
// Example
const lockKey = `user-lock:${userId}`;
const lockValue = $ctx.$user.id;
const acquired = await $ctx.$cache.acquire(lockKey, lockValue, 10000); // 10 seconds
if (acquired) {
try {
// Critical operation here
} finally {
await $ctx.$cache.release(lockKey, lockValue);
}
}
Check Cache Exists
const exists = await $ctx.$cache.exists(key, value);
// Example
const lockExists = await $ctx.$cache.exists('user-lock:123', 'user-456');
Delete Cache Key
await $ctx.$cache.deleteKey(key);
// Example
await $ctx.$cache.deleteKey('user:123');
Next Steps
- See Logging & Error Handling for logging and error throwing
- Check Advanced Features for file uploads and more
- Learn about Cache Operations for detailed cache patterns