1 | # OvernightJS
|
2 |
|
3 | :warning: __OvernightJS/logger is deprecated. Please use its offshoot jet-logger.__
|
4 |
|
5 | > TypeScript decorators for the ExpressJS Web Server!
|
6 |
|
7 | <img alt='overnightjs' src='https://github.com/seanpmaxwell/overnight/raw/master/overnightjs.png' border='0'>
|
8 |
|
9 | <a href="https://www.npmjs.com/package/@overnightjs/core" target="_blank"><img src="https://img.shields.io/npm/v/@overnightjs/core.svg" alt="NPM Version" /></a>
|
10 | <a href="https://www.npmjs.com/package/@overnightjs/core" target="_blank"><img src="https://img.shields.io/npm/l/@overnightjs/core.svg" alt="Package License" /></a>
|
11 | <a href="https://www.npmjs.com/package/@overnightjs/core" target="_blank"><img src="https://img.shields.io/npm/dm/@overnightjs/core.svg" alt="NPM Downloads" /></a>
|
12 |
|
13 |
|
14 | ## What is it
|
15 | OvernightJS is a simple library to add TypeScript decorators for methods meant to call Express routes.
|
16 | It also includes a package for managing json-web-tokens and printing logs.
|
17 |
|
18 |
|
19 | ## Features
|
20 | * Define a base route using a @Controller decorator.
|
21 | * Decorators to convert class methods to Express routes (@Get, @Put, @Post, @Delete etc).
|
22 | * Method decorators also work with arrow functions set as class properties.
|
23 | * @Middleware and @ClassMiddleware decorators.
|
24 | * @ErrorMiddleware and @ErrorMiddleware decorators to handle request errors.
|
25 | * Add options to controllers the same as you would Express routers with @ClassOptions.
|
26 | * Support for child-controllers with @ChildControllers.
|
27 | * @Wrapper and @ClassWrapper decorators to wrap functions.
|
28 | * Server superclass to initialize ExpressJS server and setup controllers.
|
29 | * Allows for adding your own custom Router classes if you don't want to use the standard express Router.
|
30 | * Easy to configure logging tool.
|
31 | * Json-Web-Token management.
|
32 | * Master repo includes a sample application, if you want to practice with an API calling tool such as Postman.
|
33 | * Compatible with both es5 and es6.
|
34 | * Fully type safe :)
|
35 |
|
36 |
|
37 | ## Why OvernightJS
|
38 | OvernightJS isn't meant to be a replacement for Express. If you're already somewhat familiar with ExpressJS, you can
|
39 | learn Overnight in about 10 minutes. There are some other frameworks which do add decorators for Express such as NestJS
|
40 | and TsExpressDecorators, but these are massive frameworks with entire websites dedicated to their documentation. OvernightJS
|
41 | is clean, simple, and aside from the decorators, you can interact with ExpressJS in the same way you would any other Node
|
42 | application.
|
43 |
|
44 |
|
45 | ## Table of Contents
|
46 | * [OvernightJS/core](#overnight-core)
|
47 | * [OvernightJS/logger](#overnight-logger)
|
48 | * [OvernightJS/jwt](#overnight-jwt)
|
49 |
|
50 |
|
51 |
|
52 | ## Installation
|
53 | You can get the latest release using npm:
|
54 |
|
55 | ```batch
|
56 | $ npm install --save @overnightjs/core express
|
57 | $ npm install --save-dev @types/express
|
58 | ```
|
59 |
|
60 | > **Important!** OvernightJS requires Node >= 6, Express >= 4, TypeScript >= 2.0 and the `experimentalDecorators`,
|
61 | `lib` compilation options in your `tsconfig.json` file.
|
62 |
|
63 |
|
64 | ## <a name="overnight-core"></a> Quick start
|
65 |
|
66 | #### Create your controller
|
67 |
|
68 | ````typescript
|
69 | import { OK, BAD_REQUEST } from 'http-status-codes';
|
70 | import { Controller, Middleware, Get, Post, Put, Delete } from '@overnightjs/core';
|
71 | import { Request, Response } from 'express';
|
72 | import { Logger } from '@overnightjs/logger';
|
73 |
|
74 | @Controller('api/users')
|
75 | export class UserController {
|
76 |
|
77 | @Get(':id')
|
78 | private get(req: Request, res: Response) {
|
79 | Logger.Info(req.params.id);
|
80 | return res.status(OK).json({
|
81 | message: 'get_called',
|
82 | });
|
83 | }
|
84 |
|
85 | @Get('')
|
86 | @Middleware([middleware1, middleware2])
|
87 | private getAll(req: ISecureRequest, res: Response) {
|
88 | Logger.Info(req.payload, true);
|
89 | return res.status(OK).json({
|
90 | message: 'get_all_called',
|
91 | });
|
92 | }
|
93 |
|
94 | @Post()
|
95 | private add(req: Request, res: Response) {
|
96 | Logger.Info(req.body, true);
|
97 | return res.status(OK).json({
|
98 | message: 'add_called',
|
99 | });
|
100 | }
|
101 |
|
102 | @Put('update-user')
|
103 | private update(req: Request, res: Response) {
|
104 | Logger.Info(req.body);
|
105 | return res.status(OK).json({
|
106 | message: 'update_called',
|
107 | });
|
108 | }
|
109 |
|
110 | @Delete('delete/:id')
|
111 | private delete(req: Request, res: Response) {
|
112 | Logger.Info(req.params, true);
|
113 | return res.status(OK).json({
|
114 | message: 'delete_called',
|
115 | });
|
116 | }
|
117 |
|
118 | @Get(/ane/) // Rexes supported. Matches /lane, /cane, etc.
|
119 | public getAne(req: Request, res: Response): any {
|
120 | return res.status(OK).json({
|
121 | message: '/ane/',
|
122 | });
|
123 | }
|
124 |
|
125 | @Get('practice/async')
|
126 | private async getWithAsync(req: Request, res: Response) {
|
127 | try {
|
128 | const asyncMsg = await this.asyncMethod(req);
|
129 | return res.status(OK).json({
|
130 | message: asyncMsg,
|
131 | });
|
132 | } catch (err) {
|
133 | Logger.Err(err, true);
|
134 | return res.status(BAD_REQUEST).json({
|
135 | error: err.message,
|
136 | });
|
137 | }
|
138 | }
|
139 |
|
140 | private asyncMethod(req: Request): Promise<string> {
|
141 | return new Promise((resolve) => {
|
142 | resolve(req.originalUrl + ' called');
|
143 | });
|
144 | }
|
145 | }
|
146 | ````
|
147 |
|
148 | - You don't have to use class methods, you can also use class properties whose value is an arrow function. You will
|
149 | have to cast Overnight to the 'any' type to avoid type errors though.
|
150 |
|
151 | ````typescript
|
152 | import * as OvernightJS from '@overnightjs/core';
|
153 |
|
154 | ...
|
155 |
|
156 | @(OvernightJS as any).Get('arrow/:id')
|
157 | private get = (req: Request, res: Response) => {
|
158 | this.logger.info(req.params.id);
|
159 | return res.status(200).json({msg: 'get_arrow_called'});
|
160 | }
|
161 | ````
|
162 |
|
163 | - If you want your middleware to apply to every route in a class use the `@ClassMiddleware` decorator.
|
164 |
|
165 | ````typescript
|
166 | import { Controller, ClassMiddleware } from '@overnightjs/core';
|
167 |
|
168 | @Controller('api/users')
|
169 | @ClassMiddleware([middleware1, middleware2])
|
170 | export class UserController {
|
171 |
|
172 | ...
|
173 | }
|
174 | ````
|
175 |
|
176 | - You can set the `@ErrorMiddleware` / `@ClassErrorMiddleware` decorators to use [Express error handling](https://expressjs.com/en/guide/error-handling.html).
|
177 |
|
178 | ````typescript
|
179 | import { Controller, ErrorMiddleware, ClassErrorMiddleware } from '@overnightjs/core';
|
180 |
|
181 | @Controller('api/users')
|
182 | @ClassErrorMiddleware(errorMiddleware1)
|
183 | export class UserController {
|
184 |
|
185 | @Get(':id')
|
186 | @ErrorMiddleware(errorMiddleware2)
|
187 | private get(req: Request, res: Response)
|
188 | ...
|
189 | }
|
190 | ````
|
191 |
|
192 | - Child-controllers can be added with the `@ChildControllers` decorator. There's no limit to how
|
193 | many levels of nesting you can add. Make sure to instantiate them before adding them. Options at the
|
194 | class level can be added with `@ClassOptions` decorator.
|
195 |
|
196 | ````typescript
|
197 | import { Controller, ClassOptions, ChildControllers } from '@overnightjs/core';
|
198 | import { ChildController1, ChildController2 } from '...'
|
199 |
|
200 | @Controller('api/users')
|
201 | @ClassOptions({mergeParams: true})
|
202 | @ChildControllers([
|
203 | new ChildController1(),
|
204 | new ChildController2(),
|
205 | ])
|
206 | export class ParentController {
|
207 |
|
208 | ...
|
209 | }
|
210 | ````
|
211 |
|
212 | - You can wrap each class method in a custom function with the `@Wrapper` decorator. If you use the `@ClassWrapper`
|
213 | decorator then every method in that class will be wrapped with the provided method.
|
214 |
|
215 | ````typescript
|
216 | import * as expressAsyncHandler from 'express-async-handler';
|
217 | import { ClassWrapper, Controller, Get, Wrapper } from '@overnightjs/core';
|
218 | import { Request, Response } from 'express';
|
219 |
|
220 | @Controller('wrapper-practice')
|
221 | // Or instead of using @Wrapper below you could use @ClassWrapper here
|
222 | export class WrapperController {
|
223 |
|
224 | @Get('async-third-party/:id')
|
225 | @Wrapper(expressAsyncHandler)
|
226 | private async asyncThirdParty(req: Request, res: Response) {
|
227 | const asyncMsg = await someAsyncFunction();
|
228 | return res.status(200).json({
|
229 | message: asyncMsg,
|
230 | });
|
231 | }
|
232 | }
|
233 | ````
|
234 |
|
235 | #### Import your controllers into the server
|
236 | OvernightJS provides a Server superclass which initializes a new ExpressJS application. The express
|
237 | object is accessed using `this.app`, which is a protected, readonly class variable. You can interact
|
238 | with this variable like you would any normal express Application created with `require('express')()`.
|
239 | If you want to print to the console the name of each controller that has been successfully configured,
|
240 | set `showLogs` to `true` via the `this.showLogs` setter or the Server `constructor()`.
|
241 | <br>
|
242 |
|
243 | `super.addControllers()` must be called to enable all of the routes in your controller. Make sure to
|
244 | call it after setting up your middleware. You can pass `super.addControllers()` a single controller-instance
|
245 | or an array of controller-instances, but they must be instantiated first.
|
246 | <br>
|
247 |
|
248 | ````typescript
|
249 | import * as bodyParser from 'body-parser';
|
250 | import { Server } from '@overnightjs/core';
|
251 | import { Logger } from '@overnightjs/logger';
|
252 | import { UserController } from './UserController';
|
253 | import { SignupController } from './SignupController';
|
254 |
|
255 | export class SampleServer extends Server {
|
256 |
|
257 | constructor() {
|
258 | super(process.env.NODE_ENV === 'development'); // setting showLogs to true
|
259 | this.app.use(bodyParser.json());
|
260 | this.app.use(bodyParser.urlencoded({extended: true}));
|
261 | this.setupControllers();
|
262 | }
|
263 |
|
264 | private setupControllers(): void {
|
265 | const userController = new UserController();
|
266 | const signupController = new SignupController();
|
267 | const dbConnObj = new SomeDbConnClass('credentials');
|
268 | signupController.setDbConn(dbConnObj);
|
269 | userController.setDbConn(dbConnObj);
|
270 | // super.addControllers() must be called, and can be passed a single controller or an array of
|
271 | // controllers. Optional router object can also be passed as second argument.
|
272 | super.addControllers(
|
273 | [userController, signupController],
|
274 | /*, optional router here*/,
|
275 | /* middleware that will apply to all controllers here */,
|
276 | );
|
277 | }
|
278 |
|
279 | public start(port: number): void {
|
280 | this.app.listen(port, () => {
|
281 | Logger.Imp('Server listening on port: ' + port);
|
282 | })
|
283 | }
|
284 | }
|
285 | ````
|
286 |
|
287 | **IMPORTANT NOTE:** If you initialize environment variables from some script which imports the
|
288 | Server script, those environment variables must be configured before importing the Server script
|
289 | or else they could end up undefined for nested controllers.
|
290 |
|
291 | #### See how awesome this is!
|
292 | Without the above decorators we would have to wrap each controller method with something like:
|
293 |
|
294 | ````typescript
|
295 | /* In the controller file*/
|
296 | class UserController {
|
297 |
|
298 | public getRoutes(): Router {
|
299 | const router = Router();
|
300 | router.get('/', [your middleware], (req, res) => {
|
301 | // Do some stuff in here
|
302 | });
|
303 | router.get('/anotherRoute', [other middleware], (req, res) => {
|
304 | // Do some stuff in here
|
305 | });
|
306 | // Repeat for every single controller method
|
307 | return router;
|
308 | }
|
309 | }
|
310 |
|
311 | let userController = new UserController();
|
312 | this.app.use('/api/users', userController.getRoutes());
|
313 | // Repeat for every single controller class
|
314 | ````
|
315 |
|
316 | This would get really tedious overtime and lead to a lot of boiler plate code.
|
317 |
|
318 |
|
319 | #### <a name="custom-router"></a> Using a Custom Router
|
320 | Suppose you don't want to use the built in "Router" object which is provided by Express. Maybe you
|
321 | want use _express-promise-router_ because you don't like using `try/catch` blocks. OvernightJS allows
|
322 | you to pass in a custom router object in the `super.addControllers()` method. Simply pass in your
|
323 | custom router as the second param after the controller-instance/s. When you don't specify a custom
|
324 | router, the default express.Router() object is used.
|
325 |
|
326 |
|
327 | - Controller using _express-promise-router_:
|
328 |
|
329 | ````typescript
|
330 | import { Request, Response } from 'express';
|
331 | import { Controller, Get } from '@overnightjs/core';
|
332 |
|
333 | @Controller('api/posts')
|
334 | export class PostController {
|
335 |
|
336 | @Get(':id')
|
337 | private get(req: Request, res: Response) {
|
338 | return this.someAsyncFunction(req.params.id)
|
339 | .then(ret => res.status(200).json({msg: ret}));
|
340 | }
|
341 |
|
342 | private someAsyncFunction(id: number): Promise<string> {
|
343 | return new Promise((res, rej) => {
|
344 | isNaN(id) ? rej('Invalid id') : res('Valid id');
|
345 | })
|
346 | }
|
347 | }
|
348 | ````
|
349 |
|
350 | - Add _express-promise-router_ in the `super.addControllers()` method:
|
351 |
|
352 | ````typescript
|
353 | import * as customRouter from 'express-promise-router';
|
354 | import { Server } from '@overnightjs/core';
|
355 | import { PostController } from './controllers/PostController';
|
356 |
|
357 | export class CustomRouterServer extends Server {
|
358 |
|
359 | constructor() {
|
360 | super();
|
361 | super.addControllers(new PostController(), customRouter); // <-- custom router added here
|
362 | }
|
363 |
|
364 | ...
|
365 | }
|
366 | ````
|
367 | <br>
|
368 | <br>
|
369 | <br>
|
370 |
|
371 |
|
372 |
|
373 | ## <a name="overnight-logger"></a> OvernightJS/logger
|
374 | Despite the abundance of logging tools out there, knowing exactly which is the right one for your
|
375 | web-server might take more time than you feel like spending. So you can start logging events right
|
376 | away, OvernightJS comes with its own logging package. From the environment variables you can easily
|
377 | switch your logs to be printed out to the command line, a file, sent through your own custom logging
|
378 | logic, or turned off completely. Logs printed to the console also are printed out in different colors
|
379 | depending on whether they're info, a warning, an error, etc. The file for holding logs can be specified
|
380 | manually or left as the default. Let's check it out!<br>
|
381 |
|
382 | ### Installation
|
383 | ```batch
|
384 | $ npm install --save @overnightjs/logger
|
385 | ```
|
386 |
|
387 | ### Guide
|
388 | The logger package's main export is the `Logger` class. Logger can used statically or as an instance
|
389 | with settings configured through a constructor.
|
390 |
|
391 | - The three environment variables are:
|
392 | - `OVERNIGHT_LOGGER_MODE`: can be `'CONSOLE'`(default), `'FILE'`, `'CUSTOM'`, and `'OFF'`.
|
393 | - `OVERNIGHT_LOGGER_FILEPATH`: the file-path for file mode. Default is _home_dir/overnight.log_.
|
394 | - `OVERNIGHT_LOGGER_RM_TIMESTAMP`: removes the timestamp next to each log. Can be `'TRUE'` or `'FALSE'`(default).
|
395 |
|
396 | _logger_ has an export `LoggerModes` which is an enum that provides all the modes if you want to
|
397 | use them in code. I would recommend using `Console` for local development, `File` for remote development,
|
398 | and `Custom` or `Off` for production. If you want to change the settings in code, you can do so via
|
399 | the constructor or getters/setters.
|
400 | <br>
|
401 |
|
402 |
|
403 | - There are 4 functions on Logger to print logs. Each has a static counterpart:
|
404 | - `info` or `Info`: prints green.
|
405 | - `imp` or `Imp`: prints magenta.
|
406 | - `warn` or `Warn`: prints yellow.
|
407 | - `err` or `Err`: prints red.
|
408 |
|
409 | There is an optional second param to each method which is a `boolean`. If you pass `true` as the second
|
410 | param, Logger will use node's `util` so that the full object gets printed. You should NOT normally
|
411 | use this param, but it is especially useful when debugging errors so that you can print out the full
|
412 | error object and observe the stack trace.<br>
|
413 |
|
414 | Let's look at a code sample which sets the environment variables via a start script:
|
415 |
|
416 | - In the start script
|
417 | ````typescript
|
418 | import * as path from 'path';
|
419 | import * as fs from 'fs';
|
420 | import { LoggerModes } from '@overnightjs/logger';
|
421 |
|
422 | // Set the
|
423 | const logFilePath = path.join(__dirname, '../sampleProject.log');
|
424 | process.env.OVERNIGHT_LOGGER_MODE = LoggerModes.File; // Can also be Console, Custom, or Off
|
425 | process.env.OVERNIGHT_LOGGER_FILEPATH = logFilePath;
|
426 |
|
427 | // Remove current log file if it exists
|
428 | (function removeFile() {
|
429 | try {
|
430 | fs.unlinkSync(logFilePath);
|
431 | } catch (e) { return; }
|
432 | })();
|
433 | ````
|
434 |
|
435 | - In the controller file
|
436 | ````typescript
|
437 | import { OK } from 'http-status-codes';
|
438 | import { Request, Response } from 'express';
|
439 | import { Controller, Get } from '@overnightjs/core';
|
440 | import { Logger } from '@overnightjs/logger';
|
441 |
|
442 | @Controller('api/logger')
|
443 | export class LoggerPracticeController {
|
444 |
|
445 | private readonly logger: Logger;
|
446 |
|
447 | constructor() {
|
448 | this.logger = new Logger();
|
449 | }
|
450 |
|
451 | @Get('static/console/:msg')
|
452 | private printLogsConsole(req: Request, res: Response) {
|
453 | Logger.Info(req.params.msg);
|
454 | Logger.Imp(req.params.msg);
|
455 | Logger.Warn(req.params.msg);
|
456 | Logger.Err(req.params.msg);
|
457 | Logger.Err(new Error('printing out an error'));
|
458 | Logger.Err(new Error('printing out an error full'), true); // <-- print the full Error object
|
459 | return res.status(OK).json({
|
460 | message: 'static_console_mode',
|
461 | });
|
462 | }
|
463 |
|
464 | @Get('console/:msg')
|
465 | private printLogsConsole(req: Request, res: Response) {
|
466 | this.logger.info(req.params.msg);
|
467 | this.logger.imp(req.params.msg);
|
468 | this.logger.warn(req.params.msg);
|
469 | this.logger.err(req.params.msg);
|
470 | this.logger.err(new Error('printing out an error'));
|
471 | this.logger.err(new Error('printing out an error full'), true);
|
472 | return res.status(OK).json({
|
473 | message: 'console_mode',
|
474 | });
|
475 | }
|
476 | }
|
477 | ````
|
478 |
|
479 | - The previous code-snippet will show the following content when printed to a file:
|
480 | ````
|
481 | IMPORTANT: [2019-04-07T19:17:28.799Z]: OvernightJS with standard express router started on port: 3000
|
482 | INFO: [2019-04-07T19:18:08.939Z]: hello-logger
|
483 | IMPORTANT: [2019-04-07T19:18:08.939Z]: hello-logger
|
484 | WARNING: [2019-04-07T19:18:08.939Z]: hello-logger
|
485 | ERROR: [2019-04-07T19:18:08.940Z]: hello-logger
|
486 | ERROR: [2019-04-07T19:18:08.940Z]: Error: printing out an error
|
487 | ERROR: [2019-04-07T19:18:08.956Z]: Error: printing out an error full
|
488 | at class_1.LoggerPracticeController.printLogsFile (/home/seanmaxwell/WebstormProjects/Overnight/sample-project/src/controllers/LoggerPracticeController.ts:49:20)
|
489 | at class_1.descriptor.value [as printLogsFile] (/home/seanmaxwell/WebstormProjects/Overnight/src/core/lib/PropertyDecorators.ts:36:35)
|
490 | at callBack (/home/seanmaxwell/WebstormProjects/Overnight/src/core/lib/Server.ts:78:50)
|
491 | at Layer.handle [as handle_request] (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/layer.js:95:5)
|
492 | at next (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/route.js:137:13)
|
493 | at Route.dispatch (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/route.js:112:3)
|
494 | at Layer.handle [as handle_request] (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/layer.js:95:5)
|
495 | at /home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/index.js:281:22
|
496 | at param (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/index.js:354:14)
|
497 | at param (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/index.js:365:14)
|
498 | at Function.process_params (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/index.js:410:3)
|
499 | at next (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/index.js:275:10)
|
500 | at Function.handle (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/index.js:174:3)
|
501 | at router (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/index.js:47:12)
|
502 | at Layer.handle [as handle_request] (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/layer.js:95:5)
|
503 | at trim_prefix (/home/seanmaxwell/WebstormProjects/Overnight/src/core/node_modules/express/lib/router/index.js:317:13)
|
504 | ````
|
505 |
|
506 |
|
507 | - And this when printed to the console:
|
508 | <img alt='overnightjs' src='https://github.com/seanpmaxwell/overnight/raw/master/loggerConsole.png' border='0'>
|
509 |
|
510 |
|
511 | ### Using a custom logger
|
512 | For production you'll probably have some third party logging tool like ElasticSearch or Splunk. _logger_ exports
|
513 | one interface `ICustomLogger` which has one method `sendLog()` that needs to implemented. If you created a class
|
514 | which implements this interface, and add it to Logger through a setter or the constructor, and set the mode to `CUSTOM`,
|
515 | Logger will call whatever logic you created for `sendLog()`.
|
516 |
|
517 | ````typescript
|
518 | // CustomLoggerTool.ts
|
519 | import { ICustomLogger } from '@overnightjs/logger';
|
520 |
|
521 | export class CustomLoggerTool implements ICustomLogger {
|
522 |
|
523 | private readonly thirdPartyLoggingApplication: ThirdPartyLoggingApplication;
|
524 |
|
525 | constructor() {
|
526 | this.thirdPartyLoggingApplication = new ThirdPartyLoggingApplication();
|
527 | }
|
528 |
|
529 | // Needs to be implemented
|
530 | public sendLog(content: any, prefix: string): void {
|
531 | // prefix is either: INFO | ERROR | WARNING | IMPORTANT
|
532 | this.thirdPartyLoggingApplication.doStuff(content);
|
533 | }
|
534 | }
|
535 | ````
|
536 |
|
537 | ````typescript
|
538 | // In the controller file
|
539 |
|
540 | ...
|
541 |
|
542 | @Get('useCustomLogger/:msg')
|
543 | private useCustomLogger(req: Request, res: Response) {
|
544 | const logger = new Logger(LoggerModes.CUSTOM, '', true, this.customLoggerTool);
|
545 | logger.rmTimestamp = true;
|
546 | logger.info(req.params.msg);
|
547 | return res.status(OK).json({
|
548 | message: 'console_mode',
|
549 | });
|
550 | }
|
551 | ````
|
552 | <br>
|
553 | <br>
|
554 | <br>
|
555 |
|
556 |
|
557 |
|
558 | ## <a name="overnight-jwt"></a> OvernightJS/jwt
|
559 |
|
560 | ### What is it
|
561 | This is an easy tool for removing boilerplate code around json-web-tokens (jwt). You can get your token
|
562 | and middleware with just one line of code. @overnightjs/core is a sister library to add TypeScript decorators
|
563 | for methods meant to call Express routes and is not required for @overnightjs/jwt but they do work beautifully
|
564 | together.
|
565 |
|
566 |
|
567 | ### Features
|
568 | * `JwtManager` class which, when when used statically, can pull the JWT expiration and secret from
|
569 | the environment variables.
|
570 | * When used as an instance-object, `JwtManager` can be dynamically passed the JWT expiration and secret
|
571 | if you prefer to set them through the code.
|
572 | * Default values for the secret and expiration when used statically. This is convenient for a
|
573 | development environment.
|
574 | * Fully type-safe :)
|
575 |
|
576 |
|
577 | ### Installation
|
578 | ```batch
|
579 | $ npm install --save @overnightjs/jwt express
|
580 | $ npm install --save-dev @types/express @types/express-jwt @types/jsonwebtoken
|
581 | ```
|
582 |
|
583 | ### Table of Contents
|
584 | * [Option 1](#option-1)
|
585 | * [Option 2](#option-2)
|
586 |
|
587 |
|
588 | #### <a name="options-1"></a> Option 1: Environment Variables
|
589 | This is what really saves you from having to do boilerplate code. The two environment variables you
|
590 | need to set are **OVERNIGHT_JWT_SECRET** and **OVERNIGHT_JWT_EXP**. OVERNIGHT_JWT_SECRET should be a really
|
591 | long, random string (recommended is 80 characters) and the rules for setting OVERNIGHT_JWT_EXP are the same as
|
592 | setting the expiration time for the _jsonwebtoken_ library. The rules are:
|
593 |
|
594 | > If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds is used by
|
595 | default ("120" is equal to "120ms"). Examples: "2 days", "10h", "7d".
|
596 |
|
597 | How you set your environment variables will vary depending on the which environment you are working in.
|
598 | I use Ubuntu which is pretty easy. Just open the _/etc/environment_ file and type:
|
599 |
|
600 | > OVERNIGHT_JWT_SECRET="your super long random string" <br>
|
601 | > OVERNIGHT_JWT_EXP="your expiration time"
|
602 |
|
603 | Another common option is the `dotenv` library, which imports environment variables from a _.env_ file
|
604 | <br>
|
605 |
|
606 | If you do not set these environment variables, a default value of **'3 days'** will be set for the expiration time and a
|
607 | random string will be generated for the secret. The random string is fine for development but do not use it for
|
608 | production. Every time you restart the server the secret will change and all client-side JWTs will become invalid.
|
609 | <br>
|
610 |
|
611 | Now let's create the controller. The data that is encrypted is stored as the `payload` property. That's all there is to it.
|
612 | Just import `JwtManager`.
|
613 |
|
614 |
|
615 | ````typescript
|
616 | import { OK } from 'http-status-codes';
|
617 | import { JwtManager, ISecureRequest } from '@overnightjs/jwt';
|
618 | import { Controller, Middleware, Get, Post } from '@overnightjs/core';
|
619 | import { Request, Response } from 'express';
|
620 |
|
621 | @Controller('api/jwt')
|
622 | export class JwtPracticeController {
|
623 |
|
624 | @Get(':email')
|
625 | private getJwt(req: Request, res: Response) {
|
626 | const jwtStr = JwtManager.jwt({
|
627 | email: req.params.email
|
628 | });
|
629 | return res.status(OK).json({
|
630 | jwt: jwtStr,
|
631 | });
|
632 | }
|
633 |
|
634 | @Post('callProtectedRoute')
|
635 | @Middleware(JwtManager.middleware)
|
636 | private callProtectedRoute(req: ISecureRequest, res: Response) {
|
637 | return res.status(OK).json({
|
638 | email: req.payload.email,
|
639 | });
|
640 | }
|
641 | }
|
642 | ````
|
643 |
|
644 | #### <a name="options-2"></a> Option 2: Pass through the constructor
|
645 | If you want to set your secret and expiration time manually, you can instantiate the `JwtManager` class
|
646 | and set them via the constructor. I love using Option 1 way more, but I thought I'd supply this option
|
647 | for people who prefer to import it another way.
|
648 |
|
649 | ````typescript
|
650 | import { OK } from 'http-status-codes';
|
651 | import { JwtManager, ISecureRequest } from '@overnightjs/jwt';
|
652 | import { Controller, Middleware, Get, Post } from '@overnightjs/core';
|
653 | import { Request, Response } from 'express';
|
654 |
|
655 | const jwtMgr = new JwtManager('secret', '10h');
|
656 |
|
657 | @Controller('api/jwt')
|
658 | export class JwtPracticeController {
|
659 |
|
660 | @Get('getJwtAlt/:fullname')
|
661 | private getJwtFromHandler(req: Request, res: Response) {
|
662 | const jwtStr = jwtMgr.jwt({
|
663 | fullName: req.params.fullname
|
664 | });
|
665 | return res.status(OK).json({
|
666 | jwt: jwtStr,
|
667 | });
|
668 | }
|
669 |
|
670 | @Post('callProtectedRouteAlt')
|
671 | @Middleware(jwtMgr.middleware)
|
672 | private callProtectedRouteFromHandler(req: ISecureRequest, res: Response) {
|
673 | return res.status(OK).json({
|
674 | fullname: req.payload.fullName,
|
675 | });
|
676 | }
|
677 | }
|
678 | ````
|
679 |
|
680 |
|
681 | #### Works just as fine in regular Express
|
682 | You dont have to use `@overnightjs/jwt` with `@overnightjs/core`. If you're using Express but are not
|
683 | interested in using decorators, you can pass the middleware just the same as you would for any typical
|
684 | Express Router object.
|
685 |
|
686 | ````javascript
|
687 | const router = express.Router();
|
688 |
|
689 | router.get('users', JwtManager.middleware, (req, res) => {
|
690 | console.log(req.payload.email);
|
691 | })
|
692 |
|
693 | app.use('/', router);
|
694 | ````
|
695 |
|
696 |
|
697 | ## That's All!!
|
698 | Please star this repo if you found it useful. Happy web-deving :)
|
699 |
|
700 |
|
701 | ## License
|
702 | [MIT](LICENSE)
|
703 |
|
\ | No newline at end of file |