Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork0
TypeNexus is a great tool for API encapsulation and management. It offers a clean and lightweight way to bundle TypeORM + Expressjs functionality, helping you to build applications faster while reducing template code redundancy and type conversion work.
License
jaywcjlove/typenexus
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
TypeNexus is a great tool for API encapsulation and management. It offers a clean and lightweight way to bundleTypeORM +Express.js functionality, helping you to build applications faster while reducing template code redundancy and type conversion work.
$ npm install typenexus
Its important to set these options intsconfig.json file of your project:
{"emitDecoratorMetadata":true,"experimentalDecorators":true}
Its important to set these options inpackage.json file of your project:
{"type":"module",}
import{TypeNexus}from'typenexus';(async()=>{constapp=newTypeNexus();awaitapp.start();// Open in browser http://localhost:3000})();
Entity is a class that maps to a database table (or collection when usingPostgres). You can create an entity by defining a new class and mark it with@Entity():
./src/user.entity.ts
import{Entity,PrimaryGeneratedColumn,Column,CreateDateColumn}from'typenexus';// OR:import{Entity,PrimaryGeneratedColumn,Column,CreateDateColumn}from'typeorm';@Entity()exportclassUser{ @PrimaryGeneratedColumn()id:number; @Column({unique:true})username:string; @Column({select:false})password:string; @CreateDateColumn()createAt:Date;}
./src/user.controller.ts
import{TypeNexus,Controller,Param,Body,DataSource}from'typenexus';import{Get,Post,Put,Delete,Patch,Delete,Head}from'typenexus';import{User}from'./user.entity.js';@Controller('/api/users')exportclassUserController{constructor(@DSource()privatedataSource:DataSource){} @Get()// => GET /api/userspublicasyncgetAll():Promise<User[]>{returnthis.dataSource.manager.find(User);} @Get('/:id')// => GET /api/users/:idpublicasyncgetById(@Param('id')id:string):Promise<User>{returnthis.dataSource.manager.findOne(User,id);} @Post('/:id')// => POST /api/users/:idpublicasyncmodify(@Body()body:{name:string;}):Promise<{name:string;}>{return{name:body.name+'~~'}} @Put('/:id')// => PUT /api/users/:idpublicasyncmodify(@Param('id')id:string):Promise<{uid:string;}>{return{uid:id}} @Delete('/:id')// => DELETE /api/users/:idpublicasyncmodify(@Param('id')id:string):Promise<{uid:string;}>{return{uid:id}} @Patch('/:id')// => PATCH /api/users/:idpublicasyncpatch():Promise<any>{return{id:12}} @Head('/:id')// => HEAD /api/users/:idpublicasynchead():Promise<{id:number;}>{return{id:12}}}
This class will register routes specified in method decorators in your server frameworkExpress.js.
./src/index.ts
import{TypeNexus}from'typenexus';import{UserController}from'./user.controller.js';;(async()=>{constapp=newTypeNexus();// ❶ Performs connection to the database.awaitapp.connect({type:'postgres',host:process.env.HOST||'localhost',port:5432,username:process.env.DB_USER||'postgres',password:process.env.DB_PASS||'wcjiang',database:process.env.DB_NAME||'typenexus-base',synchronize:true,logging:true,entities:['dist/entity/*.js'],// OR:// entities: [User],});// ❷ 🚨 Please be sure to use it after `app.connect()`.app.controllers([UserController]);// ❸ Listen for connections.awaitapp.start();})();
Open in browserhttp://localhost:3000/users. You will see This action returns all users in your browser. If you openhttp://localhost:3000/api/users/1 you will see This action returns user data.
└── src ├── user.controller.ts ├── user.entity.ts └── index.ts
Your interaction with the database is only possible once you setup aDataSource.TypeORM'sDataSource holds your database connection settings and establishes initial database connection or connection pool depending on theRDBMS you use.
import{TypeNexus}from'typenexus';importcryptofrom'crypto';importUserfrom'./entity/User.js'constapp=newTypeNexus(3000,{ ....});awaitapp.connect();// You can use the DataSource example here.// 🚨 Please be sure to use it after `app.connect()`.constrepos=app.dataSource.getRepository(User);// Check if there is an admin account.constadminUser=awaitrepos.findOneBy({username:'wcj'});if(!adminUser){consthashPassword=crypto.createHmac('sha256','1234').digest('hex');// Create an admin account.constuser=awaitrepos.create({username:'wcj',name:'管理员',password:hashPassword,});awaitrepos.save(user);}// 🚨 Please be sure to use it after `app.connect()`.app.controllers([UserController]);awaitapp.start();
Useapp.dataSource to get theDataSource instance.
dataSourceOptions is a data source configuration you pass when you create a newDataSource instance. DifferentRDBMS-es have their own specific options.
import{TypeNexus,TypeNexusOptions}from'typenexus';constoptions:TypeNexusOptions={dataSourceOptions:{type:'postgres',host:process.env.POSTGRES_HOST||'localhost',port:5432,username:process.env.POSTGRES_USER||'postgres',password:process.env.POSTGRES_PASSWORD||'wcjiang',database:process.env.POSTGRES_DB||'typenexus-base',synchronize:true,logging:true,entities:['dist/entity/*.js'],// entities: [User],},};(async()=>{constapp=newTypeNexus(3000,options);awaitapp.connect();app.controllers([UserController]);app.express.disable('x-powered-by');awaitapp.start();})();
It can also be passed as a parameter insidethe app.connect() method:
awaitapp.connect({ ...});
Entity is a class that maps to a database table (or collection when usingPostgres). You can create an entity by defining a new class and mark it with@Entity():
import{Entity,PrimaryGeneratedColumn,Column}from"typenexus"@Entity()exportclassUser{ @PrimaryGeneratedColumn()id:number @Column()firstName:string @Column()lastName:string @Column()isActive:boolean}
This will create following database table:
+-------------+--------------+----------------------------+| user|+-------------+--------------+----------------------------+| id| int(11)| PRIMARY KEY AUTO_INCREMENT|| firstName| varchar(255)||| lastName| varchar(255)||| isActive| boolean||+-------------+--------------+----------------------------+
import{DataSourceOptions}from'typeorm';import{OptionsUrlencoded,OptionsJson,OptionsText,Options}from'body-parser';import{SessionOptions}from"express-session";exportinterfaceTypeNexusOptions{port?:number;/** Global route prefix, for example '/api'. */routePrefix?:string;/** DataSourceOptions is an interface with settings and options for specific DataSource. */dataSourceOptions?:DataSourceOptions;/** Create a session middleware */session?:SessionResult|SessionCallback;/** * Indicates if default TypeNexus's error handler is enabled or not. * Enabled by default. */defaultErrorHandler?:boolean;/** * Indicates if TypeNexus should operate in development mode. */developmentMode?:boolean;/** Node.js body parsing middleware. */bodyParser?:{/** * Returns middleware that parses all bodies as a string and only looks at requests where the Content-Type header matches the type option. */text?:OptionsText;/** * Returns middleware that parses all bodies as a Buffer and only looks at requests where the Content-Type header matches the type option. */raw?:Options;/** * Returns middleware that only parses json and only looks at requests where the Content-Type header matches the type option. */json?:false|OptionsJson;/** * Returns middleware that only parses urlencoded bodies and only looks at requests where the Content-Type header matches the type option * Used for parsing request bodies in application/x-www-form-urlencoded format. *@default `{extended:false}` */urlencoded?:false|OptionsUrlencoded;};/** * Indicates if cors are enabled. * This requires installation of additional module (cors for express). */cors?:boolean|CorsOptions;/** Node.js compression middleware. The following compression codings are supported: deflate | gzip */compression?:false|CompressionOptions;/** Default settings */defaults?:{/** * If set, all null responses will return specified status code by default */nullResultCode?:number;/** * If set, all undefined responses will return specified status code by default */undefinedResultCode?:number;};/** * Special function used to check user authorization roles per request. * Must return true or promise with boolean true resolved for authorization to succeed. */authorizationChecker?:(action:Action,roles:any[])=>Promise<boolean>|boolean;/** * Special function used to get currently authorized user. */currentUserChecker?:(action:Action)=>Promise<any>|any;}
Example of parameter configuration:
newTypeNexus(3000,{routePrefix:'api'});
@Req() decorator injects you aRequest object, and@Res() decorator injects you aResponse object. If you have installed typings, you can use their types:
import{Controller,Req,Res,Get}from'typeorm';import{Response,Request}from'express';@Controller()exportclassUserController{ @Get('/users')// => GET /usersgetAllUsers(@Req()request:Request, @Res()response:Response){returnresponse.send('Hello response!');} @Get('/posts')// => GET /postsgetAllPosts(@Req()request:Request, @Res()response:Response){// some response functions don't return the response object,// so it needs to be returned explicitlyresponse.redirect('/users');returnresponse;}}
You can use framework's request and response objects directly. If you want to handle the response by yourself, just make sure you return the response object itself from the action.
If you want to prefix all your routes, e.g./api you can use routePrefix option:
import{TypeNexus}from'typenexus';import{UserController}from'./controller/User.js';;(async()=>{constapp=newTypeNexus(3033);// 🚨 Be sure to put it in front of `app.controllers()`app.routePrefix='/api'app.controllers([UserController]);})();
You can also achieve the same effect by configuringroutePrefix in the parameters when instantiatingTypeNexus:
constapp=newTypeNexus(3033,{routePrefix:'/api'});
You can prefix all specific controller's actions with base route:
import{Controller,Get}from'typeorm';@Controller('/api')exportclassUserController{ @Get("/users/:id")// => GET /api/users/12publicasyncgetOne(){} @Get("/users")// => GET /api/userspublicasyncgetUsers(){}// ...}
@DSource() decorator injects you aDataSource object.
Support constructor@DSource() decorator
import{Controller,Get,DSource,DataSource}from'typenexus';import{Response,Request}from'express';import{User}from'../entity/User.js';@Controller('/users')exportclassUserController{constructor(@DSource()privatedataSource:DataSource){} @ContentType('application/json') @Get()// => GET /userspublicasyncgetUsers():Promise<User[]>{returnthis.dataSource.manager.find(User);}}
Support parameter@DSource() decorator
import{Controller,Get,DSource,DataSource}from'typenexus';import{Response,Request}from'express';import{User}from'../entity/User.js';@Controller('/users')exportclassUserController{ @Get()// => GET /userspublicasyncgetUsers(@DSource()dataSource:DataSource):Promise<User[]>{returndataSource.manager.find(User);}}
To inject request body, use@Body decorator:
import{Controller,Post,Body}from'typeorm';typeUserBody={username:string;id:number;};@Controller()exportclassUserController{ @Post("/users")// => POST /userssaveUser(@Body()user:UserBody){// ...}}
To inject request body parameter, use@BodyParam decorator:
import{Controller,Post,BodyParam}from'typeorm';typeUserBody={username:string;id:number;};@Controller()exportclassUserController{ @Post("/users")// => POST /userssaveUser(@BodyParam("name")userName:string){// ...}}
To inject request header parameter, use@HeaderParam decorator:
import{Controller,Post,HeaderParam}from'typeorm';@Controller()exportclassUserController{ @Post("/users")saveUser(@HeaderParam("authorization")token:string){// ...}}
If you want to inject all header parameters use@HeaderParams() decorator.
To inject query parameters, use@QueryParam decorator:
import{Controller,Get,QueryParam}from'typeorm';typeUserBody={username:string;id:number;};@Controller()exportclassUserController{ @Get("/users")publicasyncgetUsers(@QueryParam("limit")limit:number){// ....}}
If you want to inject all query parameters use@QueryParams() decorator.
import{Controller,Get,QueryParams}from'typeorm';@Controller()exportclassUserController{ @Get("/users")publicasyncgetUsers(@QueryParams()query:any){// ....}}
You can use@Param decorator to inject parameters in your controller actions:
import{Controller,Get,Param}from'typeorm';@Controller()exportclassUserController{ @Get("/users/:id")getOne(@Param("id")id:string){}}
If you want to inject all parameters use@Params() decorator.
To get a cookie parameter, use@CookieParam decorator:
import{Controller,Get,CookieParam,CookieParams}from'typeorm';@Controller()exportclassUserController{ @Get("/users")publicasyncgetUsers(@CookieParam("token")token:string){// ....}}
If you want to inject all header parameters use@CookieParams() decorator.
To inject a session value, use@SessionParam decorator:
@Get("/login")savePost(@SessionParam("user")user:User, @Body()post:Post){}
If you want to inject the main session object, use@Session() without any parameters.
@Get("/login")savePost(@Session()session:any, @Body()post:Post){}
Express usesexpress-session to handle session, so firstly you have to install it manually to use@Session decorator. Here is an example of configuringSession, and you need to create a database table entity forSession as well:
import{TypeNexus,DataSourceOptions}from'typenexus';import{UserController}from'./controller/User.js';import{Session}from'./entity/Session.js';constoptions:TypeNexusOptions={// ...dataSourceOptions:{ ...},session:{secret:'secret',resave:false,saveUninitialized:false,repositoryTarget:Session,typeormStore:{cleanupLimit:2,// limitSubquery: false, // If using MariaDB.ttl:86400,}}};(async()=>{constapp=newTypeNexus(3001,options);// ❶ Performs connection to the database.awaitapp.connect();// OR:// await app.connect(options.dataSourceOptions);// ❷ 🚨 Please be sure to use it after `app.connect()`.app.controllers([UserController]);// ❸ Listen for connections.awaitapp.start();})();
Here is the database table entity for Session:
// ./entity/Session.jsimport{Column,Entity,Index,PrimaryColumn,DeleteDateColumn}from'typeorm';import{ISession}from'connect-typeorm';@Entity()exportclassSessionimplementsISession{ @Index() @Column('bigint',{transformer:{from:Number,to:Number}})publicexpiredAt=Date.now(); @PrimaryColumn('varchar',{length:255})publicid=''; @DeleteDateColumn()publicdestroyedAt?:Date; @Column('text')publicjson='';}
To inject uploaded file, use@UploadedFile decorator:
@Post("/file")saveFile(@UploadedFile("fileName")file:Express.Multer.File){}
To inject uploaded multiple files, use the@UploadedFiles decorator:
@Post("/files")saveFiles(@UploadedFiles("fileName")file:Express.Multer.File[]){}
You can also specify uploading options tomulter this way:
importtype{Options}from'multer';// to keep code clean better to extract this function into separate fileconstfileUploadOptions:()=>Options=()=>({storage:multerFn.diskStorage({destination:(req,file,cb)=>{//...},filename:(req,file,cb)=>{//...}}),fileFilter:(req,file,cb)=>{//...},limits:{fieldNameSize:255,fileSize:1024*1024*2}});// use options this way:@Post("/file")saveFiles(@UploadedFile("fileName",fileUploadOptions)file:Express.Multer.File){}
To inject all uploaded files use@UploadedFiles decorator instead.typenexus usesmulter to handle file uploads.
You can set aLocation header for any action:
import{Controller,Get,Location}from'typenexus';@Controller()exportclassUserController{ @Get('/users') @Location("https://bing.com")publicasyncdetail(){}}
Sets the response Location HTTP header to the specified path parameter.
You can set aRedirect header for any action:
import{Controller,Get,Redirect}from'typenexus';@Controller()exportclassUserController{ @Get('/users') @Redirect("http://github.com")publicasyncdetail(){}}
You can override theRedirect header by returning a string value:
import{Controller,Get,Redirect}from'typenexus';@Controller()exportclassUserController{ @Get('/users') @Redirect("http://github.com")publicasyncdetail(){return"https://bing.com";}}
You can use template to generate theRedirect header:
import{Controller,Get,Redirect}from'typenexus';@Controller()exportclassUserController{ @Get('/users') @Redirect("http://github.com/:owner/:repo")publicasyncdetail(){return{owner:"jaywcjlove",repo:"typenexus"};}}
You can explicitly set a returned HTTP code for any action:
import{Controller,Post,HttpCode}from'typenexus';@Controller()exportclassUserController{ @Post('/users') @HttpCode(201)publicasyncsaveUser(){}}
If your controller returnsvoid orPromise<void> or undefined it will throw you404 error. To prevent this if you need to specify what status code you want to return using@OnUndefined decorator.
import{Controller,Param,Delete,OnUndefined,DSource,DataSource}from'typeorm';import{User}from'../entity/User.js';@Controller()exportclassUserController{constructor(@DSource()privatedataSource:DataSource){} @Delete("/users/:id") @OnUndefined(204)asyncremove(@Param("id")id:string):Promise<void>{returnthis.dataSource.manager.findOneBy(User,{ id});}}
@OnUndefined is also useful when you return some object which can or cannot beundefined. In this examplefindOneBy returnsundefined in the case if user with given id was not found. This action will return404 in the case if user was not found, and regular200 in the case if it was found.
import{Controller,Param,Delete,OnUndefined,DSource,DataSource}from'typeorm';import{User}from'../entity/User.js';@Controller()exportclassUserController{constructor(@DSource()privatedataSource:DataSource){} @Delete("/users/:id") @OnUndefined(404)asyncremove(@Param("id")id:string):Promise<void>{returnthis.dataSource.manager.findOneBy(User,{ id});}}
You can also specify error class you want to use if it returnedundefined:
import{HttpError}from'typeorm';exportclassUserNotFoundErrorextendsHttpError{constructor(){super(404,'User not found!');}}
import{Controller,Param,Delete,OnUndefined,DSource,DataSource}from'typeorm';import{User}from'../entity/User.js';@Controller()exportclassUserController{constructor(@DSource()privatedataSource:DataSource){} @Get("/users/:id") @OnUndefined(UserNotFoundError)asyncremove(@Param("id")id:string):Promise<void>{returnthis.dataSource.manager.findOneBy(User,{ id});}}
If controller action returnsnull you can use@OnNull decorator instead.
import{Controller,Get,OnNull,Param}from'typeorm';@Controller()exportclassUserController{ @Get('/questions/:id') @OnNull(404)publicasyncdetail(@Param('id')id:string):Promise<string>{returnnewPromise((ok,fail)=>{ok(null);});}}
You can set any custom header in a response:
import{Controller,Get,Header,Param}from'typeorm';@Controller()exportclassUserController{ @Get("/users/:id") @Header("Cache-Control","none")publicasyncgetOne(@Param('id')id:string):Promise<string>{// ...}}
If you are using server-side rendering you canrender any template:
import{Controller,Get,Render}from'typenexus';@Controller('/')exportclassUserController{ @Get() @Render("index")getOne(){return{title:"these params are used"};}}
To use rendering ability make sure to configureexpress properly. To use rendering ability withexpress you will need to use a rendering 3rd party such asejs,pug is the only render middleware that has been tested.
$ npm install ejs
The directory where the template files are located. Eg:app.set('views', './views'). This defaults to theviews directory in the application root directory.
app.express.set('views',path.join(__dirname,'views'));
The template engine to use. For example, to use theejs template engine:app.set('view engine', 'ejs').
app.express.set('view engine','ejs');
Create aejs template file namedindex.ejs in the views directory, with the following content:
<!DOCTYPE html><html> <head> <title><%= title%></title> </head> <body> <h1><%= title%></h1> <p>Welcome to<%= title%></p> </body></html>
Complete entry example:
import{TypeNexus}from'typenexus';importpathfrom'node:path';import{fileURLToPath}from'node:url';import{UserController,CustomErrorHandler}from'./UserController.js';const__filename=fileURLToPath(import.meta.url);const__dirname=path.dirname(__filename);;(async()=>{constapp=newTypeNexus(3002,{defaultErrorHandler:false,});app.express.set('views',path.join(__dirname,'views'));app.express.set('view engine','ejs');app.controllers([UserController],[CustomErrorHandler]);awaitapp.start();})();
If you want to return errors with specific error codes, there is an easy way:
import{Controller,Get,Header,Param}from'typeorm';@Controller()exportclassUserController{ @Get("/users/:id")publicasyncgetOne(@Param('id')id:string):Promise<string>{constuser=awaitdataSource.manager.findOneBy(User,{ id});if(!user){thrownewNotFoundError(`User was not found.`);// message is optional}returnuser;}}
Now, when user won't be found with requested id, response will be with http status code404 and following content:
{"name":"NotFoundError","message":"User was not found."}
There are set of prepared errors you can use:
HttpErrorBadRequestErrorForbiddenErrorInternalServerErrorMethodNotAllowedErrorNotAcceptableErrorNotFoundErrorUnauthorizedError
You can also create and use your own errors by extendingHttpError class. To define the data returned to the client, you could define a toJSON method in your error.
classDbErrorextendsHttpError{publicoperationName:string;publicargs:any[];constructor(operationName:string,args:any[]=[]){super(500);Object.setPrototypeOf(this,DbError.prototype);this.operationName=operationName;this.args=args;// can be used for internal logging}toJSON(){return{status:this.httpCode,failedOperation:this.operationName,};}}
Since CORS is a feature that is used almost in any web-api application, you can enable it intypenexus options.
import{TypeNexus,Action}from'typenexus';import{UserController}from'./UserController.js';;(async()=>{constapp=newTypeNexus(3002,{cors:true,});app.controllers([UserController]);awaitapp.start();})();
You can also configurecors:
import{TypeNexus,Action}from'typenexus';import{UserController}from'./UserController.js';;(async()=>{constapp=newTypeNexus(3002,{cors:{// options from cors documentation},});app.controllers([UserController]);awaitapp.start();})();
TypeNexus comes with two decorators helping you to organize authorization in your application.
To make@Authorized decorator to work you need to setup specialTypeNexus options:
constapp=newTypeNexus(3002,{ ...});awaitapp.connect();app.authorizationChecker=async(action:Action,roles:string[])=>{// here you can use request/response objects from action// also if decorator defines roles it needs to access the action// you can use them to provide granular access check// checker must return either boolean (true or false)// either promise that resolves a boolean value// demo code:consttoken=action.request.query.token||action.request.body.token||(action.request.headers.authorization||'').replace(/^token\s/,'');if(action.request.session.token!==token)returnfalse;constdataSource=action.dataSource;constuser=awaitdataSource.manager.findOne(User,{where:{ username},select:['username','id','roles'],});if(user&&roles.find(role=>user.roles.indexOf(role)!==-1))returntrue;//@ts-ignoreif(action.request.session.token===token)returntrue;returnfalse;}app.controllers([UserController]);awaitapp.start();
You can use@Authorized on controller actions:
import{Controller,Authorized,Req,Res,Get}from'typeorm';import{Response,Request}from'express';@Controller()exportclassUserController{ @Authorized('POST_MODERATOR')// you can specify roles or array of roles @Post('/posts')// => POST /postscreate(@Body()post:Post, @Req()request:Request, @Res()response:Response){// ...}}
To make@CurrentUser decorator to work you need to setup specialTypeNexus options:
import{TypeNexus,Action}from'typenexus';import{UserController}from'./UserController.js';import{User}from'./User.js';;(async()=>{constapp=newTypeNexus(3002,{routePrefix:'/api',developmentMode:false,});app.currentUserChecker=async(action:Action)=>{returnnewUser(1,'Johny','Cage');}app.controllers([UserController]);awaitapp.start();})();
You can use@CurrentUser on controller actions:
import{Controller,CurrentUser,Get}from'typenexus';import{User}from'./User.js';@Controller('/questions')exportclassUserController{ @Get()publicasyncall(@CurrentUser()user?:User):Promise<any>{return{id:1,title:'Question by '+user.firstName,};}}
If you mark@CurrentUser asrequired andcurrentUserChecker logic will return empty result, thenTypeNexus will throw authorization required error.
You can use any existingexpress middleware, or create your own. To create your middlewares there is a@Middleware decorator, and to use already exist middlewares there are@UseBefore and@UseAfter decorators.
There are multiple ways to use middleware. For example, lets try to usecompression middleware:
- Install compression middleware:
$ npm install compression
- To use middleware per-action:
import{Controller,Get,UseBefore}from"typeorm";importcompressionfrom'compression';@Controller()exportclassUserController{ @Get('/users/:id') @UseBefore(compression())asyncgetOne(@Param("id")id:string):Promise<any>{// ...}}
This way compression middleware will be applied only forgetOne controller action, and will be executed before action execution. To execute middleware after action use@UseAfter decorator instead.
- To use middleware per-controller:
import{Controller,UseBefore}from"typeorm";importcompressionfrom'compression';@Controller()@UseBefore(compression())exportclassUserController{}
This way compression middleware will be applied for all actions of theUserController controller, and will be executed before its action execution. Same way you can use@UseAfter decorator here.
- If you want to use compression module globally for all controllers you can simply register it during bootstrap:
import{TypeNexus,Action}from'typenexus';import{UserController}from'./UserController.js';;(async()=>{constapp=newTypeNexus(3002,{routePrefix:'/api',developmentMode:false,});app.controllers([UserController]);app.express.use(compression());awaitapp.start();})();
Alternatively, you can create a customglobal middleware and simply delegate its execution to the compression module.
Here is example of creating middleware for express.js:
- There are two ways of creating middleware:
First, you can create a simple middleware function:
import{Request,Response,NextFunction}from'express';exportfunctionloggingMiddleware(request:Request,response:Response,next?:NextFunction):any{console.log('do something...');next();}
Second you can create a class:
import{ExpressMiddlewareInterface}from'typenexus';exportclassMyMiddlewareimplementsExpressMiddlewareInterface{// interface implementation is optionaluse(request:Request,response:Response,next?:NextFunction):any{console.log('do something...');next();}}
- Then you can use them this way:
import{Controller,UseBefore,UseAfter}from'typeorm';import{MyMiddleware,MyMiddleware2}from'./MyMiddleware';import{loggingMiddleware}from'./loggingMiddleware';@Controller()@UseBefore(MyMiddleware,MyMiddleware2)@UseAfter(loggingMiddleware)exportclassUserController{}
- or per-action:
import{Controller,UseBefore,UseAfter,Get}from'typeorm';import{MyMiddleware}from'./MyMiddleware';import{loggingMiddleware}from'./loggingMiddleware';@Controller()exportclassUserController{ @Get("/users/:id") @UseBefore(MyMiddleware) @UseAfter(loggingMiddleware)getOne(@Param("id")id:string){// ...}}
@UseBefore executes middleware before controller action.@UseAfter executes middleware after each controller action.
Global middlewares run before each request, always. To make your middleware global mark it with@Middleware decorator and specify if it runs after or before controllers actions.
import{ExpressMiddlewareInterface}from'typenexus';import{Request,Response,NextFunction}from'express';@Middleware({type:'before'})exportclassLoggingMiddlewareimplementsExpressMiddlewareInterface{use(request:Request,response:Response,next:NextFunction):void{console.log('do something...');//@ts-ignorerequest.test='wcj';next();}}
To enable this middleware, specify it duringtypenexus initialization:
import{TypeNexus}from'typenexus';import'./LoggingMiddleware.js';constapp=newTypeNexus(3002,{routePrefix:'/api',developmentMode:false,});
Or register withapp.controllers().
import{TypeNexus}from'typenexus';import{LoggingMiddleware}from'./LoggingMiddleware.js';import{UserController}from'./UserController.js';constapp=newTypeNexus(3002,{routePrefix:'/api',developmentMode:false,});app.controllers([UserController],[LoggingMiddleware]);
Error handlers are specific only to express. Error handlers work same way as middlewares, but implementExpressErrorMiddlewareInterface:
Create a class that implements theErrorMiddlewareInterface interface:
import{Middleware,ExpressErrorMiddlewareInterface}from'typenexus';import{Request,Response,NextFunction}from'express';@Middleware({type:'after'})exportclassCustomErrorHandlerimplementsExpressErrorMiddlewareInterface{error(error:any,request:Request,response:Response,next:NextFunction):void{response.status(error.status||500);next();}}
Custom error handlers are invoked after the default error handler, so you won't be able to change response code or headers. To prevent this, you have to disable default error handler by specifyingdefaultErrorHandler option inTypeNexusOptions:
import{TypeNexus}from'typenexus';constapp=newTypeNexus(3002,{routePrefix:'/api',developmentMode:false,defaultErrorHandler:false,// disable default error handler, only if you have your own error handler});
As always, thanks to our amazing contributors!
Made withcontributors.
This package is licensed under the MIT License.
About
TypeNexus is a great tool for API encapsulation and management. It offers a clean and lightweight way to bundle TypeORM + Expressjs functionality, helping you to build applications faster while reducing template code redundancy and type conversion work.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors3
Uh oh!
There was an error while loading.Please reload this page.