- Notifications
You must be signed in to change notification settings - Fork0
forker-man/nestjs-common
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
In this repo you will find a lot of the base shared code that we will user throughout all of our NestJS projects. Some of these common modules that we have bundled are:
- TryCatch Decorators
- HTTP Filters
- Authentication Guards
- Common Exceptions
- Error Handling Service
- Pagination Classes
- Validation Pipes
- Redis Service
npm i @teamhive/nestjs-common
From there just add whatever you want to import into your Core/Common Modules
There are several peer dependencies of this project. Once you install this package you will need to follow up and ensure the follow dependencies are installed:
npm i @nestjs/common@^7.0 @nestjs/core@^7.0 @nestjs/passport@^7.0 @nestjs/testing@^7.0 @nestjs/microservices@^7.0 @teamhive/nestjs-swagger@^4.6.0 class-validator@^0.12.2 config@^3.2 js-yaml@^3.0 log4js@^6.2 passport@^0.4 reflect-metadata@^0.1 rxjs@^6.5
There are also a few dev dependencies that you may want to add in order for typescript to compile correctly:
npm i --save-dev @types/config @types/raven
There are several configurations that we use throughout our projects. Some of them are required by this package. Here is what you should add into the default config file (https://www.npmjs.com/package/config)
application:name:AppNameport:8080apiPrefix:'/api'raven:dsn:'https://logger.sentry.io/31'authentication:jwt:accessExpiration:28800# 8 hoursrefreshExpiration:2592000# 1 monthsession:accessCookie:name:'access_token'options:httpOnly:trueexpires:falsesecure:truemaxAge:28800000# 8 hoursrefreshCookie:name:'refresh_token'options:httpOnly:trueexpires:falsesecure:truelogger:appenders:out:type:stdoutcategories:default:appenders: -outlevel:errorsql:appenders: -outlevel:errorredis:host:'localhost'keyPrefix:'app_name_'expiration:86400000# ms - 24 hours
There are a few different decorators that we have made available:
This decorator will wrap your whole function into a try/catch and you can pass an optional custom error class for it to throw! Errors that are thrownthat extend this package's BaseException are not re-wrapped.
@TryCatch(SqlException)asyncfetchAll(){returnawaitthis.usersRepository.fetchAll()}
This decorator will pull out the query parameters and the req.user object and inject them into the DTO
asyncfetchAll(@QueryUser()query:FetchAllPgDto){returnawaitthis.usersRepository.fetchAll(query)}
This decorator will pull out the req.user object and make them available in the method
asyncfetchAll(@User()user:AuthorizedUser){returnawaitthis.usersRepository.fetchAll(user)}
This decorator will typically be used in tandem with the PermissionsGuard so that you can ensure the route is protected based on certain permissions
@Permissions('CONTENT_VIEW') @UseGuards(IsLoggedInGuard,PermissionsGuard) @Get()asyncfetchAll(@Query()query:ContentFetchAllPgDto){constcontent=awaitthis.contentService.fetchAll(query);returnnewContentFetchAllAndCount(content);}
This decorator will inject metadata into an object on the the request.
@Get()asyncfetchAll( @InjectMetadata('query',injectUser)assignmentPgDto:AdminDto){constassignments=awaitthis.adminAssignmentService.fetchAll(assignmentPgDto);returnfindAndCountAllResponse(assignments,AdminAssignmentFetchAllResponse);}
In the example above, the user can be injected into the request's query so the Dto has access to all of that data while being built automatically by Nest.
The decorator accepts the following arguments:
(reqProperty?:string, ...injectFunctions:(req,paramTarget,paramProperty,paramIndex)=>object)[]
wherereqProperty
is the name of the property on the request to inject data into and theinjectFunctions
are functions that given the request and the decorator data for the decorated method return an object to be merged into the metadata for the request property's value.
In your project, it is recommend that you create anInjectableMetadata
interface that defines all of the possible metadata that could be injected into request property.These should match up with the keys that the inject functions return their data on. Below is an example of what that might look like.
exportconstinjectUser=(req)=>({user:req.user});exportconstinjectIsAdmin=(_req,target)=>({isAdmin:Reflect.getMetadata('isAdmin',target.constructor)});exportinterfaceInjectableMetadata{user:AuthenticatedUser;isAdmin:boolean;}
To access this data in your DTO, you should redefine the InjectedDto type provided by this library and use your InjectableMetadata interface.
import{InjectedDtoasCommonInjectedDto}from'@teamhive/nestjs-common';import{InjectableMetadata}from'./injectable-metadata';exporttypeInjectedDto<DtoType,PickedFieldsextendskeyofInjectableMetadata>=CommonInjectedDto<DtoType,InjectableMetadata,PickedFields>;
Then in the DTO, use this as the type of the argument that is passed into the constructor. All of the injected metadata can then be found andappropriately typed on theINJECTED_METADATA_KEY
.
import{INJECTED_METADATA_KEY}from'@teamhive/nestjs-common';import{InjectedDto}from'../../common';exportclassAdminDto{constructor(options:InjectedDto<PaginationOptions,'user'|'isAdmin'>){const{ user,isAdmin:adminMode}=options[INJECTED_METADATA_KEY];}}
This class is our standard that we use for pagination requests. You will want to extend this class for your custom pagination classes
Note: If your application tends to sort by 'ASC', in your main.ts set the static propertydefaultSortDir = 'ASC'
exportclassUserFetchAllPgDtoextendsPagination{ @IsOptional() @IsString() @IsIn(['firstName','lastName'])sortBy:string; @ValidateNested()filter:UserFetchAllFilter;constructor(pagination:PaginationOptions={}){super('firstName',pagination);try{this.filter=newUserFetchAllFilter(JSON.parse(decodeURI(pagination.filter)));}catch(e){this.filter=newUserFetchAllFilter();}this.search=newSearch(UserSearch,pagination.search).include||null;}}
This guard extends the @nestjs/passport AuthGuard and provides a better error handling pattern. You will want to extend this class for any custom guards in your application and override the handleErrors method to report any non-whitelisted errors. You can also customize the whitelisted errors with the addToWhitelist and removeFromWhitelist methods. By default, this will throw an UnauthorizedException, but you can change that functionality by overriding the throwException method.
import{AuthGuard}from'@nestjs/passport';import{UnauthorizedException}from'../exceptions';import{PassportWhitelistedErrors}from'../enums';exportfunctionPassportAuthGuard(strategyToken:string){returnclassGuardextendsAuthGuard(strategyToken){whitelistedErrors:Set<string>;constructor(){super();this.whitelistedErrors=newSet(Object.values(PassportWhitelistedErrors));}handleRequest(error,user,passportError){if(error||!user){// if the error is not an instance of our UnauthorizedException, then captureif(!(errorinstanceofUnauthorizedException)){constactualError=error||(passportErrorinstanceofError) ?passportError :false||newError('Passport Error');// only handle (report to sentry) if not a whitelisted errorif(!this.isWhitelisted(actualError)){this.handleErrors(actualError);}}this.throwException();}returnuser;}isWhitelisted(error={}asError){returnArray.from(this.whitelistedErrors).some(whitelistedError=>{if(typeoferror.message==='string'){returnerror.message.includes(whitelistedError);}returnfalse;});}handleErrors(error:Error){console.warn('Override the handleErrors method in the child class of PassportAuthGuard to report errors!');console.error(error);}throwException(){thrownewUnauthorizedException();}addToWhitelist(messages:string[]){messages.forEach(message=>this.whitelistedErrors.add(message));}removeFromWhitelist(messages:PassportWhitelistedErrors[]){messages.forEach(message=>this.whitelistedErrors.delete(String(message)));}};}
npm packnpm version (major|minor|patch)npm publish
Note: This will also automatically push changes and tags post-publish
About
Our common decorators, services, etc for NestJS projects
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Languages
- TypeScript100.0%