Package Exports
- exguard-cached
- exguard-cached/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (exguard-cached) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
ExGuard Cached
NestJS backend package for caching user data and permissions in Redis. Provides fast, cache-first access to user roles, groups, and permissions using the guard: key prefix.
Installation
npm install exguard-cachedRequired Environment Variables
Create a .env file in your NestJS backend root:
# Redis Connection
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password
REDIS_DB=0
REDIS_URL=redis://localhost:6379
# Cache Settings
REDIS_KEY_PREFIX=guard:
REDIS_TTL=300
CACHE_DEFAULT_TTL=300
CACHE_KEY_PREFIX=guard:
CACHE_ENABLE_LOGGING=false
# Permission Caching
PERMISSION_CACHE_ENABLED=true
PERMISSION_CACHE_TTL=300Key Notes:
REDIS_KEY_PREFIXandCACHE_KEY_PREFIXmust beguard:(default)- Cache keys follow format:
guard:user:{cognitoSubId} - Permissions are stored as:
module:resource:action(e.g.,exGUARD:user_groups:update_group)
Files to Configure
1. app.module.ts - Main Application Module
import { Module } from '@nestjs/common';
import { ExGuardCacheModule } from 'exguard-cached';
import { RedisModule } from './redis.module'; // Your Redis module
@Module({
imports: [
RedisModule, // Your Redis client module
ExGuardCacheModule.forRoot({
redisClient: redisClient, // Inject your Redis client
config: {
cache: {
defaultTtl: 300,
keyPrefix: 'guard:',
enableLogging: false,
},
permissions: {
enableCaching: true,
cacheTtl: 300,
},
},
}),
],
})
export class AppModule {}2. redis.module.ts - Redis Client Setup
import { Module, Global } from '@nestjs/common';
import { createClient } from 'redis';
@Global()
@Module({
providers: [
{
provide: 'REDIS_CLIENT',
useFactory: async () => {
const client = createClient({
socket: {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379'),
},
password: process.env.REDIS_PASSWORD,
database: parseInt(process.env.REDIS_DB || '0'),
});
await client.connect();
return client;
},
},
],
exports: ['REDIS_CLIENT'],
})
export class RedisModule {}3. Controller Implementation
import { Controller, Get, Post, Body, Param, Injectable } from '@nestjs/common';
import {
RequireCachedUserPermission,
CachedUser,
CachedUserData,
CachedUserService
} from 'exguard-cached';
@Controller('users')
export class UsersController {
constructor(private cachedUserService: CachedUserService) {}
// Example 1: Get current user data from cache
@Get('me')
@RequireCachedUserPermission('exGUARD', 'user_groups:view_group')
async getMyProfile(@CachedUser() cachedUser: CachedUserData) {
return {
user: cachedUser.user,
roles: cachedUser.roles,
groups: cachedUser.groups,
permissions: cachedUser.permissions,
fieldOffice: cachedUser.user?.fieldOffice,
};
}
// Example 2: Check specific permission
@Get('check-permission/:module/:action')
async checkPermission(
@Param('module') module: string,
@Param('action') action: string,
@CachedUser() cachedUser: CachedUserData,
) {
const hasPermission = cachedUser.permissions.some(p =>
p.includes(module) && p.includes(action)
);
return { hasPermission, module, action };
}
// Example 3: Get user's field office
@Get('field-office')
async getFieldOffice(@CachedUser() cachedUser: CachedUserData) {
return {
fieldOffice: cachedUser.user?.fieldOffice || null,
};
}
// Example 4: Admin endpoint with strict permission
@Get('admin/all')
@RequireCachedUserPermission('exGUARD', 'user_roles:view_all')
async getAllUsers(@CachedUser() cachedUser: CachedUserData) {
// Only accessible if user has exGUARD:user_roles:view_all permission
return {
message: 'Admin access granted',
userRoles: cachedUser.roles,
userModules: cachedUser.modules,
};
}
}4. Service Usage (Optional)
import { Injectable } from '@nestjs/common';
import { CachedUserService } from 'exguard-cached';
@Injectable()
export class MyService {
constructor(private cachedUserService: CachedUserService) {}
async getUserFromCache(cognitoSubId: string) {
const cachedUser = await this.cachedUserService.getCachedUser(cognitoSubId);
if (!cachedUser) {
// Cache miss - fetch from database and cache it
return null;
}
return cachedUser;
}
async setUserCache(cognitoSubId: string, userData: any) {
await this.cachedUserService.setCachedUser(cognitoSubId, userData, 300);
}
async hasPermission(cognitoSubId: string, module: string, action: string) {
return await this.cachedUserService.hasPermission(cognitoSubId, module, action);
}
async invalidateUser(cognitoSubId: string) {
await this.cachedUserService.invalidateCachedUser(cognitoSubId);
}
}Cache Key Format
guard:user:{cognitoSubId}Example: guard:user:09eaa52c-50d1-7054-5769-8819ac43eeed
Permission Format in Cache
Permissions are stored as strings in the format: module:resource:action
Examples from cached data:
exGUARD:user_groups:update_groupexGUARD:user_roles:view_allexGUARD:modules:create
To check permission: Use hasPermission(cognitoSubId, 'exGUARD', 'update_group') which searches across all modules.
CachedUserData Structure
interface CachedUserData {
user: {
id: string;
cognitoSubId: string;
username: string;
email: string;
givenName: string;
familyName: string;
employeeNumber: string;
fieldOffice: {
id: string;
code: string;
name: string;
} | null;
} | null;
groups: string[]; // ['group1', 'group2']
roles: string[]; // ['role1', 'role2']
modules: Array<{
key: string;
name: string;
permissions: string[];
}>;
fieldOffices: string[];
permissions: string[]; // ['exGUARD:user_groups:update_group', ...]
cacheInfo: {
userId: string;
cognitoSubId: string;
cacheKeys: {
access: string;
permissions: string;
};
};
}API Reference
Decorators
@RequireCachedUserPermission(module, action)
Protects endpoints by checking cached permissions.
@Get()
@RequireCachedUserPermission('exGUARD', 'user_groups:view_group')
async myMethod() { ... }@CachedUser()
Injects cached user data into the method parameter.
async myMethod(@CachedUser() user: CachedUserData) {
console.log(user.permissions);
}CachedUserService Methods
| Method | Description |
|---|---|
getCachedUser(cognitoSubId) |
Get complete cached user data |
setCachedUser(cognitoSubId, data, ttl?) |
Cache user data |
invalidateCachedUser(cognitoSubId) |
Remove user from cache |
hasPermission(cognitoSubId, module, action) |
Check if user has permission |
getUserPermissions(cognitoSubId) |
Get all user permissions |
getUserRoles(cognitoSubId) |
Get user roles |
getUserGroups(cognitoSubId) |
Get user groups |
getUserModules(cognitoSubId) |
Get user modules |
getUserFieldOffice(cognitoSubId) |
Get user's field office |
How It Works
- User logs in → Backend fetches user data from database
- Data is cached → Stored in Redis with key
guard:user:{cognitoSubId} - Subsequent requests → Check Redis first (fast), fallback to database
- Permission checks → Read from cached permissions array
- Cache invalidation → Triggered when user roles/groups change
Version History
- 1.1.0 - Current version with Redis caching and NestJS integration