Here, we have four roles: Sme, Sponsor, Admin, Operations.Initially, we had only 3 roles.Operations role was added later and Operations user has permissions similar to the Admin user.In the code, we had to replace every instance of with .As this is time consuming and we can also miss many instances, we have created a roles module. In the roles module,the roles are defined along with their respective permissions as seen in Code (Part-III). Based on the permissions for each role, we will evaluate the authorization for the user in each of our controller methods.If the user has access, only then he will be granted the resources. if (user.type == USER_TYPES.ADMIN) if (user.type == USER_TYPES.ADMIN || user.type == USER_TYPES.OPERATIONS) Code (Part-I): src/common/constants/enum.ts enum USER_TYPES { SME = , SPONSOR = , ADMIN = , OPERATIONS_TEAM = } enum ROLES_ACCESS_ACTION { USERS_CONTROLLER_FINDLIST_OPERATIONS = , USERS_CONTROLLER_FINDLIST_ADMIN = , USERS_CONTROLLER_FIND_ONE = , USERS_CONTROLLER_KYC_FILTER = , USERS_CONTROLLER_USER_STATUS_FILTER = , USERS_CONTROLLER_USER_UPDATE = , USERS_CONTROLLER_DELETE = , USERS_SERVICE_CHECK_FOR_UPDATE_STATUS_ERROR = , USERS_SERVICE_CREATE = , USERS_SERVICE_UPDATE_USER_CRMID_AND_ENTITYDETAILCODE = , REMARKS_CONTROLLER_CREATE = , REMARKS_CONTROLLER_FINDLIST = , REMARKS_CONTROLLER_FINDLIST_SME = , REMARKS_CONTROLLER_FINDLIST_SPONSOR = , SME_PROJECT_CONTROLLER_FINDLIST = , SME_PROJECT_CONTROLLER_FINDONE = , SME_PROJECT_CONTROLLER_RECOMMENDED_PROJECTS = , SME_PROJECT_CONTROLLER_CREATE = , SME_PROJECT_CONTROLLER_SPONSOR_FILTER = , SME_PROJECT_CONTROLLER_UPDATE = , BID_DETAILS_CONTROLLER_FINDLIST = , BID_DETAILS_CONTROLLER_FINDLIST_SME = , BID_DETAILS_CONTROLLER_FINDLIST_SPONSOR = , BID_DETAILS_CONTROLLER_CREATE = , BID_DETAILS_CONTROLLER_COMPLETE_BID_PROCESS = , BID_DETAILS_CONTROLLER_REJECT_ALL_BIDS_DELETE_PROJECT = , BID_DETAILS_CONTROLLER_UPDATE = , BID_DETAILS_CONTROLLER_UPDATE_SPONSOR = , BID_DETAILS_SERVICE_CALCULATE_BID_DETAILS = , BID_DETAILS_CONTROLLER_CREATE_TRANSACTION = } export "Sme" "Sponsor" "Admin" "Operations" //rolesAccessAction export "users.controller.findList_operations" "users.controller.findList_admin" "users.controller.findOne" "users.controller.findListFilterKYCStatus" "users.controller.findListFilterUserStatus" "users.controller.update" "users.controller.delete" "users.service.checkForUpdateStatusError" "users.service.create" "users.service.updateUserCrmIdAndEntityDetailCode" "remarks.controller.create" "remarks.controller.findList" "remarks.controller.findList_sme" "remarks.controller.findList_sponsor" "sme-project.controller.findList" "sme-project.controller.findOne" "sme-project.controller.getRecommendedProjects" "sme-project.controller.create" "sme-project.controller.projectSponsorFilter" "sme-project.controller.update" "bid-details.controller.findList" "bid-details.controller.findList_sme" "bid-details.controller.findList_sponsor" "bid-details.controller.create" "bid-details.controller.completeBidProcess" "bid-details.controller.rejectAllBidsDeleteProject" "bid-details.controller.update" "bid-details.controller.update_sponsor" "bid-details.controller.calculatebiddetails" "bid-details.controller.createTransaction" //BID_DETAILS_CONTROLLER_GET_FUNDED_PROJECTS = "bid-details.controller.getfundedProjects" Above, we have defined the rolesAction for each of the methods in our project.The convention used here is the controller/service name of the file followed by method name. For example, USERS_CONTROLLER_FINDLIST_OPERATIONS = "users.controller.findList_operations", we have users.controller as the controller name followed by method name as findList. Code (Part-II): src/users/users.controller.ts { Body, Controller, Param, Post, UseGuards, Get, Request, Query, Put, NotFoundException, Delete, BadRequestException, } ; { UsersService } ; { CreateUserDto, UpdateUserDto, UserDto, CloseAccount, } ; { abstractBaseControllerFactory } ; { LoggedInToken } ; { BASEROUTES, USER_TYPES, KYC_VERIFICATION_STATUS, USER_STATUS, ROLES_ACCESS_ACTION, } ; { JwtAuthGuard } ; { RequestUser, LastUpdatedTime, IdOrCodeParser, } ; { UNKNOWN_PARAM, NOT_FOUND, PAGE_NOT_FOUND_404, NEW_PASSWORD_AND_CONFIRM_NEW_PASSWORD_ERROR, USERNAME_OR_PASSWORD_INCORRECT, CURRENT_PASSWORD_AND_NEW_PASSWORD_ERROR, KYC_PENDING_STATUS_CHANGE_ERROR, KYC_APPROVED_STATUS_CHANGE_ERROR, KYC_REJECTED_STATUS_CHANGE_ERROR, USER_ACTIVE_STATUS_CHANGE_ERROR, USER_CLOSED_STATUS_CHANGE_ERROR, USER_IN_REVIEW_STATUS_CHANGE_ERROR, USER_KYC_INCOMPLETE_STATUS_CHANGE_ERROR, ONLY_FOR_ADMIN, } ; { plainToClass } ; { success } ; { AbstractClassTransformerPipe } ; { normalizeObject } ; * _ ; { InjectModel } ; { Model } ; { IUser, User } ; { CrmService } ; { KycPendingEmail } ; { EmailDto } ; { normalizePaginateResult } ; { RolesService } ; BaseController = abstractBaseControllerFactory({ : UserDto, : [ BASEROUTES.PATCH, ], }); @Controller( ) { ( private usersService: UsersService, private crmService: CrmService, @InjectModel("User") private readonly usersModel: Model, private rolesservice: RolesService ) { (usersService); } @UseGuards(JwtAuthGuard) @Get() findList(@Request() req, @Query() query, @RequestUser() user) { _user = .rolesservice.findOneByQuery({ : user.type}) hasAccessOperations = _user.rolesAccessAction.some( e === ROLES_ACCESS_ACTION.USERS_CONTROLLER_FINDLIST_OPERATIONS ); hasAccessAdmin = _user.rolesAccessAction.some( e === ROLES_ACCESS_ACTION.USERS_CONTROLLER_FINDLIST_ADMIN ); (hasAccessOperations) { .log( , user.type, _user, hasAccessOperations, hasAccessAdmin) t = { : [{ : USER_TYPES.SME }, { : USER_TYPES.SPONSOR }] }; .findList(req, { ...query, ...t }); } (hasAccessAdmin){ .findList(req, { ...query }); } NotFoundException(); } @UseGuards(JwtAuthGuard) @Get( ) findOne( @IdOrCodeParser( ) idOrCode: string, @RequestUser() user ) { _user = .rolesservice.findOneByQuery({ : user.type}); .log( , user.type, _user) hasAccess = _user.rolesAccessAction.some( e === ROLES_ACCESS_ACTION.USERS_CONTROLLER_FIND_ONE ); (hasAccess || user.code === idOrCode || user.id === idOrCode) { .findOne(idOrCode); } NotFoundException(); } @UseGuards(JwtAuthGuard) @Post( ) findListFilterKYCStatus( @Request() req, @Query() query, @RequestUser() user, @Body() body: { : number } ) { t = { : [{ : USER_TYPES.SME }, { : USER_TYPES.SPONSOR }] }; _user = .rolesservice.findOneByQuery({ : user.type}); .log( , user.type, _user) hasAccess = _user.rolesAccessAction.some( e === ROLES_ACCESS_ACTION.USERS_CONTROLLER_KYC_FILTER ); (hasAccess) { options = { : , : , : , : query.page ? (query.page - ) : }; .log( , query, query.page, body.isProfileCompleted); d = .usersModel.find( { : body.isProfileCompleted, ...t }, {}, { : { : }, : options.skip * options.limit, : options.limit, : {} } ); dCount = .usersModel.count( { : body.isProfileCompleted } ); .log(d.length, dCount); d.map( { plainToClass(UserDto, data, { : }); }); pagination = normalizePaginateResult({ : dCount, limit: options.limit, : options.page, : d.pages, }); success({ d, pagination }); } NotFoundException(); } import from "@nestjs/common" import from "./users.service" import from "./objects/create-user.dto" import from "../common/base/base.controller" import from "./objects/login-user.dto" import from "../common/constants/enum" import from "../auth/auth.guard" import from "../common/utils/controller.decorator" import from "../common/constants/string" import from "class-transformer" import from "../common/base/httpResponse.interface" import from "../common/pipes/class-transformer.pipe" import from "../common/utils/helper" import as from "lodash" import from "@nestjs/mongoose" import from "mongoose" import from "./objects/user.schema" import from "./crm/crm.service" import from "./objects/user.registered.email" import from "../email/objects/email.dto" import from "../common/interfaces/pagination" import from "../roles/roles.service" const DTO DisabledRoutes //, BASEROUTES.DETELEONE "users" export class UsersController extends BaseController constructor super async let await this roleName // if(user.type == USER_TYPES.OPERATIONS_TEAM){ let ( ) => e let ( ) => e if console 'userstype' let $or type type return await super //if (user.isAdmin) { if // <--- only admin can see the user lists return await super throw new ":idOrCode" async "idOrCode" let await this roleName console 'userstype' let ( ) => e if // <--- only admin or the same person can view a profile return await super throw new "filter/kycFilter" async isProfileCompleted let $or type type //if (user.type == USER_TYPES.ADMIN) { let await this roleName console 'userstype' let ( ) => e // console.log('userstype', user.type, _user, hasAccess) if var limit 30 page 1 sort "_id" skip 1 0 // <--- only admin and Sponsor can see all the USER lists console "filterrrrrrrrrrr" let await this "verification.isProfileCompleted" sort _id 1 skip limit projection let await this "verification.isProfileCompleted" console await ( ) => data return excludeExtraneousValues true let total //d.length, page pages return throw new In the 3 methods listed above, findList, findOne, findListFilterKYCStatus, we are checking if the user has access/authorization.For the method findListFilterKYCStatus , we are checking if the user has listed in his roles Schema as shown below in the file.Here, only users of type and have the permissions and only they are allowed access to the findListFilterKYCStatus() method. let hasAccess = _user.rolesAccessAction.some( (e) => e === ROLES_ACCESS_ACTION.USERS_CONTROLLER_KYC_FILTER ); ROLES_ACCESS_ACTION.USERS_CONTROLLER_KYC_FILTER USER_TYPES.OPERATIONS_TEAM USER_TYPES.ADMIN Code (Part-III): src/roles/roles.controller.ts { RolesDto, CreateRolesDto } ; { abstractBaseControllerFactory } ; { BASEROUTES, USER_TYPES, ROLES_ACCESS_ACTION } ; { RolesService } ; { JwtAuthGuard } ; { Controller, Get, UseGuards, Request, Query, Put, Body, Post, BadRequestException, NotFoundException, Delete, } ; { AbstractClassTransformerPipe } ; { RequestUser } ; { plainToClass } ; { success } ; BaseController = abstractBaseControllerFactory({ : RolesDto, CreateDTO: CreateRolesDto, : [ BASEROUTES.PATCH, ], }); @UseGuards(JwtAuthGuard) @Controller( ) { (private rolesservice: RolesService) { (rolesservice); } @Post() public create( @Request() req, @Body(AbstractClassTransformerPipe(CreateRolesDto)) body: any, @Query() query, @RequestUser() user ) { (body.roleName){ USER_TYPES.ADMIN: body.rolesAccessAction = [ ROLES_ACCESS_ACTION.USERS_CONTROLLER_FINDLIST_ADMIN, ROLES_ACCESS_ACTION.USERS_CONTROLLER_FIND_ONE, ROLES_ACCESS_ACTION.USERS_CONTROLLER_KYC_FILTER, ROLES_ACCESS_ACTION.USERS_CONTROLLER_USER_STATUS_FILTER, ROLES_ACCESS_ACTION.USERS_CONTROLLER_USER_UPDATE, ROLES_ACCESS_ACTION.USERS_CONTROLLER_DELETE, ROLES_ACCESS_ACTION.USERS_SERVICE_CHECK_FOR_UPDATE_STATUS_ERROR, ROLES_ACCESS_ACTION.USERS_SERVICE_CREATE, ROLES_ACCESS_ACTION.USERS_SERVICE_UPDATE_USER_CRMID_AND_ENTITYDETAILCODE, ROLES_ACCESS_ACTION.REMARKS_CONTROLLER_CREATE, ROLES_ACCESS_ACTION.REMARKS_CONTROLLER_FINDLIST, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_FINDLIST, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_FINDONE, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_UPDATE, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_FINDLIST, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_COMPLETE_BID_PROCESS, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_REJECT_ALL_BIDS_DELETE_PROJECT, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_UPDATE, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_CREATE_TRANSACTION ]; ; USER_TYPES.OPERATIONS_TEAM: body.rolesAccessAction = [ ROLES_ACCESS_ACTION.USERS_CONTROLLER_FINDLIST_OPERATIONS, ROLES_ACCESS_ACTION.USERS_CONTROLLER_FIND_ONE, ROLES_ACCESS_ACTION.USERS_CONTROLLER_KYC_FILTER, ROLES_ACCESS_ACTION.USERS_CONTROLLER_USER_STATUS_FILTER, ROLES_ACCESS_ACTION.USERS_CONTROLLER_USER_UPDATE, ROLES_ACCESS_ACTION.USERS_CONTROLLER_DELETE, ROLES_ACCESS_ACTION.USERS_SERVICE_CHECK_FOR_UPDATE_STATUS_ERROR, ROLES_ACCESS_ACTION.USERS_SERVICE_CREATE, ROLES_ACCESS_ACTION.USERS_SERVICE_UPDATE_USER_CRMID_AND_ENTITYDETAILCODE, ROLES_ACCESS_ACTION.REMARKS_CONTROLLER_CREATE, ROLES_ACCESS_ACTION.REMARKS_CONTROLLER_FINDLIST, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_FINDLIST, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_FINDONE, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_UPDATE, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_FINDLIST, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_COMPLETE_BID_PROCESS, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_REJECT_ALL_BIDS_DELETE_PROJECT, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_UPDATE, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_CREATE_TRANSACTION ]; ; USER_TYPES.SME: body.rolesAccessAction = [ ROLES_ACCESS_ACTION.USERS_SERVICE_UPDATE_USER_CRMID_AND_ENTITYDETAILCODE, ROLES_ACCESS_ACTION.REMARKS_CONTROLLER_FINDLIST_SME, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_CREATE, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_FINDLIST_SME ]; ; USER_TYPES.SPONSOR: body.rolesAccessAction = [ ROLES_ACCESS_ACTION.USERS_SERVICE_UPDATE_USER_CRMID_AND_ENTITYDETAILCODE, ROLES_ACCESS_ACTION.REMARKS_CONTROLLER_FINDLIST_SPONSOR, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_FINDLIST, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_FINDONE, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_RECOMMENDED_PROJECTS, ROLES_ACCESS_ACTION.SME_PROJECT_CONTROLLER_SPONSOR_FILTER, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_FINDLIST_SPONSOR, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_CREATE, ROLES_ACCESS_ACTION.BID_DETAILS_CONTROLLER_UPDATE_SPONSOR, ROLES_ACCESS_ACTION.BID_DETAILS_SERVICE_CALCULATE_BID_DETAILS ]; ; } roles = .rolesservice.create(body); _data = plainToClass(RolesDto, roles, { : }); success(_data); } } import from './objects/roles.dto' import from '../common/base/base.controller' import from '../common/constants/enum' import from './roles.service' import from '../auth/auth.guard' import from "@nestjs/common" import from '../common/pipes/class-transformer.pipe' import from '../common/utils/controller.decorator' import from 'class-transformer' import from '../common/base/httpResponse.interface' const DTO //Todo: Remove after creating records in Db. DisabledRoutes //Todo: Uncomment BASEROUTES.CREATE after creating records in Db. // BASEROUTES.CREATE, // BASEROUTES.DETELEONE, // BASEROUTES.UPDATEONE, 'roles' export class RolesController extends BaseController constructor super async switch case break case break case break case break let await this const excludeExtraneousValues true return The role's permissions are stored in the backend (MongoDB) Code (Part-IV): src/roles/objects/roles.schema.ts { Schema } ; { createModel, Entity, IEntity } ; { roleName: string; roleCode: string; type: string; rolesAccessAction: string[]; } interface IRoles extends Roles, IEntity { : string; } RolesSchema: Schema = createModel( , { : { : , : }, : { : , : }, : { : , : }, : [ { : , }, ] }); import from "mongoose" import from "../../common/base/base.model" export class Roles extends Entity export id export const "AdminRoles" roleName type String required true roleCode type String required true type type String required true rolesAccessAction type String This is how custom role based access is implemented without any 3rd party libraries. Link to code: https://gitlab.com/adh.ranjan/nestjs/-/tree/dSuahailTwo Also published at https://dev.to/krishnakurtakoti/custom-role-based-access-in-nest-js-mongodb-5b3b