{"id":13452481,"url":"https://github.com/seanpmaxwell/overnight","last_synced_at":"2025-05-15T11:04:45.902Z","repository":{"id":33052674,"uuid":"146218451","full_name":"seanpmaxwell/overnight","owner":"seanpmaxwell","description":"TypeScript decorators for the ExpressJS Server.","archived":false,"fork":false,"pushed_at":"2023-01-07T18:30:04.000Z","size":992,"stargazers_count":875,"open_issues_count":36,"forks_count":40,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-04-14T16:59:32.723Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seanpmaxwell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-26T21:40:27.000Z","updated_at":"2025-02-24T19:54:29.000Z","dependencies_parsed_at":"2023-01-14T23:12:28.980Z","dependency_job_id":null,"html_url":"https://github.com/seanpmaxwell/overnight","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpmaxwell%2Fovernight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpmaxwell%2Fovernight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpmaxwell%2Fovernight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpmaxwell%2Fovernight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seanpmaxwell","download_url":"https://codeload.github.com/seanpmaxwell/overnight/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248923721,"owners_count":21183951,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T07:01:25.342Z","updated_at":"2025-04-14T16:59:50.590Z","avatar_url":"https://github.com/seanpmaxwell.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# OvernightJS\r\n\r\n:warning: __OvernightJS/logger and OvernightJS/jwt are deprecated. For logging please see jet-logger.__\r\n\r\n\u003e TypeScript decorators for the ExpressJS Web Server!\r\n\r\n\u003cimg alt='overnightjs' src='https://github.com/seanpmaxwell/overnight/raw/master/overnightjs.png' border='0'\u003e\r\n\r\n\u003ca href=\"https://www.npmjs.com/package/@overnightjs/core\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@overnightjs/core.svg\" alt=\"NPM Version\" /\u003e\u003c/a\u003e\r\n\u003ca href=\"https://www.npmjs.com/package/@overnightjs/core\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/@overnightjs/core.svg\" alt=\"Package License\" /\u003e\u003c/a\u003e\r\n\u003ca href=\"https://www.npmjs.com/package/@overnightjs/core\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/@overnightjs/core.svg\" alt=\"NPM Downloads\" /\u003e\u003c/a\u003e\r\n \r\n\r\n## What is it\r\nOvernightJS is a simple library to add TypeScript decorators for methods meant to call Express routes.\r\nIt also includes a package for managing json-web-tokens and printing logs. \r\n\r\n\r\n## Features\r\n* Define a base route using a @Controller decorator.\r\n* Decorators to convert class methods to Express routes (@Get, @Put, @Post, @Delete etc).\r\n* Method decorators also work with arrow functions set as class properties.\r\n* @Middleware and @ClassMiddleware decorators.\r\n* @ErrorMiddleware and @ErrorMiddleware decorators to handle request errors.\r\n* Add options to controllers the same as you would Express routers with @ClassOptions.\r\n* Support for child-controllers with @ChildControllers.\r\n* @Wrapper and @ClassWrapper decorators to wrap functions. \r\n* Server superclass to initialize ExpressJS server and setup controllers.\r\n* Allows for adding your own custom Router classes if you don't want to use the standard express Router.\r\n* Easy to configure logging tool.\r\n* Json-Web-Token management.\r\n* Master repo includes a sample application, if you want to practice with an API calling tool such as Postman.\r\n* Compatible with both es5 and es6.\r\n* Fully type safe :)\r\n\r\n\r\n## Why OvernightJS\r\nOvernightJS isn't meant to be a replacement for Express. If you're already somewhat familiar with ExpressJS, you can\r\nlearn Overnight in about 10 minutes. There are some other frameworks which do add decorators for Express such as NestJS\r\nand TsExpressDecorators, but these are massive frameworks with entire websites dedicated to their documentation. OvernightJS\r\nis clean, simple, and aside from the decorators, you can interact with ExpressJS in the same way you would any other Node\r\napplication.\r\n\r\n\r\n\r\n## Installation\r\nYou can get the latest release using npm:\r\n\r\n```batch\r\n$ npm install --save @overnightjs/core express \r\n$ npm install --save-dev @types/express\r\n```\r\n\r\n\u003e **Important!** OvernightJS requires Node \u003e= 6, Express \u003e= 4, TypeScript \u003e= 2.0 and the `experimentalDecorators`, \r\n`lib` compilation options in your `tsconfig.json` file.\r\n\r\n\r\n## Quick start\r\n\r\n#### Create your controller\r\n\r\n````typescript\r\nimport { OK, BAD_REQUEST } from 'http-status-codes';\r\nimport { Controller, Middleware, Get, Post, Put, Delete } from '@overnightjs/core';\r\nimport { Request, Response } from 'express';\r\nimport Logger from 'jet-logger';\r\n\r\n@Controller('api/users')\r\nexport class UserController {\r\n\r\n    @Get(':id')\r\n    private get(req: Request, res: Response) {\r\n        Logger.Info(req.params.id);\r\n        return res.status(OK).json({\r\n            message: 'get_called',\r\n        });\r\n    }\r\n\r\n    @Get('')\r\n    @Middleware([middleware1, middleware2])\r\n    private getAll(req: ISecureRequest, res: Response) {\r\n        Logger.Info(req.payload, true);\r\n        return res.status(OK).json({\r\n            message: 'get_all_called',\r\n        });\r\n    }\r\n\r\n    @Post()\r\n    private add(req: Request, res: Response) {\r\n        Logger.Info(req.body, true);\r\n        return res.status(OK).json({\r\n            message: 'add_called',\r\n        });\r\n    }\r\n\r\n    @Put('update-user')\r\n    private update(req: Request, res: Response) {\r\n        Logger.Info(req.body);\r\n        return res.status(OK).json({\r\n            message: 'update_called',\r\n        });\r\n    }\r\n\r\n    @Delete('delete/:id')\r\n    private delete(req: Request, res: Response) {\r\n        Logger.Info(req.params, true);\r\n        return res.status(OK).json({\r\n            message: 'delete_called',\r\n        });\r\n    }\r\n\r\n    @Get(/ane/) // Rexes supported. Matches /lane, /cane, etc.\r\n    public getAne(req: Request, res: Response): any {\r\n        return res.status(OK).json({\r\n            message: '/ane/',\r\n        });\r\n    }\r\n\r\n    @Get('practice/async')\r\n    private async getWithAsync(req: Request, res: Response) {\r\n        try {\r\n            const asyncMsg = await this.asyncMethod(req);\r\n            return res.status(OK).json({\r\n                message: asyncMsg,\r\n            });\r\n        } catch (err) {\r\n            Logger.Err(err, true);\r\n            return res.status(BAD_REQUEST).json({\r\n                error: err.message,\r\n            });\r\n        }\r\n    }\r\n\r\n    private asyncMethod(req: Request): Promise\u003cstring\u003e {\r\n        return new Promise((resolve) =\u003e {\r\n            resolve(req.originalUrl + ' called');\r\n        });\r\n    }\r\n}\r\n````\r\n\r\n- You don't have to use class methods, you can also use class properties whose value is an arrow function. You will\r\n have to cast Overnight to the 'any' type to avoid type errors though.\r\n \r\n````typescript\r\nimport * as OvernightJS from '@overnightjs/core';\r\n\r\n    ...\r\n\r\n    @(OvernightJS as any).Get('arrow/:id')\r\n    private get = (req: Request, res: Response) =\u003e {\r\n        this.logger.info(req.params.id);\r\n        return res.status(200).json({msg: 'get_arrow_called'});\r\n    }\r\n````\r\n\r\n- If you want your middleware to apply to every route in a class use the `@ClassMiddleware` decorator. \r\n\r\n````typescript\r\nimport { Controller, ClassMiddleware } from '@overnightjs/core';\r\n\r\n@Controller('api/users')\r\n@ClassMiddleware([middleware1, middleware2])\r\nexport class UserController {\r\n    \r\n    ...\r\n}\r\n````\r\n\r\n- You can set the `@ErrorMiddleware` / `@ClassErrorMiddleware` decorators to use [Express error handling](https://expressjs.com/en/guide/error-handling.html). \r\n\r\n````typescript\r\nimport { Controller, ErrorMiddleware, ClassErrorMiddleware } from '@overnightjs/core';\r\n\r\n@Controller('api/users')\r\n@ClassErrorMiddleware(errorMiddleware1)\r\nexport class UserController {\r\n    \r\n\t@Get(':id')\r\n\t@ErrorMiddleware(errorMiddleware2)\r\n    private get(req: Request, res: Response)\r\n    ...\r\n}\r\n````\r\n\r\n- Child-controllers can be added with the `@ChildControllers` decorator. There's no limit to how \r\nmany levels of nesting you can add. Make sure to instantiate them before adding them. Options at the\r\nclass level can be added with `@ClassOptions` decorator. \r\n\r\n````typescript\r\nimport { Controller, ClassOptions, ChildControllers } from '@overnightjs/core';\r\nimport { ChildController1, ChildController2 } from '...'\r\n\r\n@Controller('api/users')\r\n@ClassOptions({mergeParams: true})\r\n@ChildControllers([\r\n    new ChildController1(), \r\n    new ChildController2(),\r\n])\r\nexport class ParentController {\r\n    \r\n    ...\r\n}\r\n````\r\n\r\n- You can wrap each class method in a custom function with the `@Wrapper` decorator. If you use the `@ClassWrapper`\r\ndecorator then every method in that class will be wrapped with the provided method. \r\n\r\n````typescript\r\nimport * as expressAsyncHandler from 'express-async-handler';\r\nimport { ClassWrapper, Controller, Get, Wrapper } from '@overnightjs/core';\r\nimport { Request, Response } from 'express';\r\n\r\n@Controller('wrapper-practice')\r\n// Or instead of using @Wrapper below you could use @ClassWrapper here\r\nexport class WrapperController {\r\n    \r\n    @Get('async-third-party/:id')\r\n    @Wrapper(expressAsyncHandler)\r\n    private async asyncThirdParty(req: Request, res: Response) {\r\n        const asyncMsg = await someAsyncFunction();\r\n        return res.status(200).json({\r\n            message: asyncMsg,\r\n        });\r\n    }\r\n}\r\n````\r\n\r\n#### Import your controllers into the server\r\nOvernightJS provides a Server superclass which initializes a new ExpressJS application. The express \r\nobject is accessed using `this.app`, which is a protected, readonly class variable. You can interact \r\nwith this variable like you would any normal express Application created with `require('express')()`.\r\nIf you want to print to the console the name of each controller that has been successfully configured,\r\nset `showLogs` to `true` via the `this.showLogs` setter or the Server `constructor()`. \r\n\u003cbr\u003e\r\n\r\n`super.addControllers()` must be called to enable all of the routes in your controller. Make sure to\r\ncall it after setting up your middleware. You can pass `super.addControllers()` a single controller-instance \r\nor an array of controller-instances, but they must be instantiated first.\r\n\u003cbr\u003e\r\n\r\n````typescript\r\nimport * as bodyParser from 'body-parser';\r\nimport { Server } from '@overnightjs/core';\r\nimport Logger from 'jet-logger';\r\nimport { UserController } from './UserController';\r\nimport { SignupController } from './SignupController';\r\n\r\nexport class SampleServer extends Server {\r\n    \r\n    constructor() {\r\n        super(process.env.NODE_ENV === 'development'); // setting showLogs to true\r\n        this.app.use(bodyParser.json());\r\n        this.app.use(bodyParser.urlencoded({extended: true}));\r\n        this.setupControllers();\r\n    }\r\n\r\n    private setupControllers(): void {\r\n        const userController = new UserController();\r\n        const signupController = new SignupController();\r\n        const dbConnObj = new SomeDbConnClass('credentials');\r\n        signupController.setDbConn(dbConnObj);\r\n        userController.setDbConn(dbConnObj);\r\n        // super.addControllers() must be called, and can be passed a single controller or an array of \r\n        // controllers. Optional router object can also be passed as second argument.\r\n        super.addControllers(\r\n            [userController, signupController],\r\n            /*, optional router here*/,\r\n            /* middleware that will apply to all controllers here */,\r\n        );\r\n    }\r\n\r\n    public start(port: number): void {\r\n        this.app.listen(port, () =\u003e {\r\n            Logger.Imp('Server listening on port: ' + port);\r\n        })\r\n    }\r\n}\r\n````\r\n\r\n**IMPORTANT NOTE:** If you initialize environment variables from some script which imports the \r\nServer script, those environment variables must be configured before importing the Server script\r\nor else they could end up undefined for nested controllers.\r\n\r\n#### See how awesome this is!\r\nWithout the above decorators we would have to wrap each controller method with something like:\r\n\r\n````typescript\r\n/* In the controller file*/\r\nclass UserController {\r\n    \r\n    public getRoutes(): Router {\r\n        const router = Router();\r\n        router.get('/', [your middleware], (req, res) =\u003e {\r\n            // Do some stuff in here\r\n        });\r\n        router.get('/anotherRoute', [other middleware], (req, res) =\u003e {\r\n            // Do some stuff in here\r\n        });\r\n        // Repeat for every single controller method\r\n        return router;\r\n    }\r\n}\r\n\r\nlet userController = new UserController();\r\nthis.app.use('/api/users', userController.getRoutes());\r\n// Repeat for every single controller class\r\n````\r\n\r\nThis would get really tedious overtime and lead to a lot of boiler plate code.\r\n\r\n\r\n#### \u003ca name=\"custom-router\"\u003e\u003c/a\u003e Using a Custom Router\r\nSuppose you don't want to use the built in \"Router\" object which is provided by Express. Maybe you\r\nwant use  _express-promise-router_ because you don't like using `try/catch` blocks. OvernightJS allows\r\nyou to pass in a custom router object in the `super.addControllers()` method. Simply pass in your\r\ncustom router as the second param after the controller-instance/s. When you don't specify a custom\r\nrouter, the default express.Router() object is used. \r\n\r\n\r\n- Controller using _express-promise-router_:\r\n\r\n````typescript\r\nimport { Request, Response } from 'express';\r\nimport { Controller, Get } from '@overnightjs/core';\r\n\r\n@Controller('api/posts')\r\nexport class PostController {\r\n\r\n    @Get(':id')\r\n    private get(req: Request, res: Response) {\r\n        return this.someAsyncFunction(req.params.id)\r\n                    .then(ret =\u003e res.status(200).json({msg: ret}));\r\n    }\r\n\r\n    private someAsyncFunction(id: number): Promise\u003cstring\u003e {\r\n        return new Promise((res, rej) =\u003e {\r\n            isNaN(id) ? rej('Invalid id') : res('Valid id');\r\n        })\r\n    }\r\n}\r\n````\r\n\r\n- Add _express-promise-router_ in the `super.addControllers()` method:\r\n\r\n````typescript\r\nimport * as customRouter  from 'express-promise-router';\r\nimport { Server } from '@overnightjs/core';\r\nimport { PostController } from './controllers/PostController';\r\n\r\nexport class CustomRouterServer extends Server {\r\n    \r\n    constructor() {\r\n        super();\r\n        super.addControllers(new PostController(), customRouter); // \u003c-- custom router added here\r\n    }\r\n\r\n    ...\r\n}\r\n````\r\n\u003cbr\u003e\r\n\u003cbr\u003e\r\n\u003cbr\u003e\r\n\r\n\r\n## That's All!!\r\nPlease star this repo if you found it useful. Happy web-deving :)\r\n\r\n\r\n## License\r\n[MIT](LICENSE)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseanpmaxwell%2Fovernight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseanpmaxwell%2Fovernight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseanpmaxwell%2Fovernight/lists"}