@m-backend/core
v1.2.0
Published
This library was generated with [Nx](https://nx.dev).
Downloads
129
Readme
This library was generated with Nx.
Boilerplate code for M's NodeJS servers.
Table of Content
- Boilerplate code for M's NodeJS servers.
Install
npm install @m-backend/core
Overview
This project allow developers to:
- Declare services
- Declare middlewares
- Use dependency injection and IoC (available in controllers, services, middlewares, application, console).
- Declare command line interface (using @Console and @Command decorators)
Dependency Injection
@m-backend/core uses the tsyringe package
to allow developers to use DI and IoC in your node project.
Services
You can declare services using the Service decorator:
@Service()
export class MyService {}
And use it in other services or controllers:
@Service()
export class OtherService {
constructor(private myService: MyService) {}
}
Middleware
You can declare a middleware class and use it in other controllers or routes handlers. A middleware class is a singleton and can use DI.
import { Context, Next } from "koa";
import { Middleware, MiddlewareInterface } from "@m-backend/core";
@Middleware()
export class MyMiddleware implements MiddlewareInterface {
use(ctx: Context, next: Next): any {
/* ... */
}
}
You can declare a middleware as application level using the autoloadIn property:
// Add the middleware to the main application (default application in the project template).
@Middleware({ autoloadIn: 'main' })
Routing
To declare a Koa route, you must create a *.controller.ts
file and export an annoted controller class as default:
@Controller()
export default class MyController {
@Get('/user/:id')
async getUserById() {/* some code here... */}
}
Controller Options
You can set options to the controller annotation.
| Option | Description |
| ------ | ----------------------------------------------------------- |
| prefix | The prefix applied to each routes defined by the controller |
| app | The application name (default to main
) |
Example:
@Controller({ prefix: '/my_route' })
/*... */
(e.g: the prefix must start with a "/
" if it's provided)
Attach middlewares
You can attach middlewares to a controller or methods with the AttachMiddleware
decorator:
@AttachMiddleware([/* a list of middleware */])
@Controller()
export default class MyController {}
Note: Middlewares attached to a controller will be called for each controller routes
@Controller()
export default class MyController {
@AttachMiddleware([/* a list of middleware */])
@Get('/my/path')
myPath(ctx: Context) {}
}
Configurations
The package exposes simple API that help load any config file. Note: please keep in mind that those configuration files should be of format .config.json)
Loading a configuration file
Use the loadConfigFile
function from the config.ts
file:
import { loadConfigFile } from '@m-backend/core';
loadConfigFile('my_config'); // Note: the expected config file name is "my_config.config.json".
Or use the loadConfigFile
method from the ConfigService
:
@Service()
export default class MyService {
constructor(private configService: ConfigService) {
this.configService.loadConfigFile('my-config');
}
}
Loading a configuration folder
Use the loadConfigFolder
function to load multiple configuration files. By default use the /config
folder or it can be custom path by giving path as function parameter
import { loadConfigFolder } from '@m-backend/core';
loadConfigFolder();
// OR
loadConfigFolder('your_path_to_configuration_folder');
File extensions are optional and can be managed by a m-backend.json
file as follow :
{
"whitelist": [".config.json", ".md"],
"blacklist": ["example.config.json", ".sql"]
}
Note :
The function
loadConfigFolder
callsloadConfigFile
after indexing files, so, some extensions can be mismanaged.
The
m-backend.json
file mustn't change, it's a dev dependency
Get a configuration
Simply:
import { config } from '@m-backend';
config.my_config
Or using the config service
@Service() // Or any other class that use the IoC (Controller/Middleware/etc...
export class MyService {
constructor(private configService: ConfigService) {
this.configService.my_config
}
}
Listen to config changes
Each config files are watched. The config service allows you to listen to these changes:
@Service() // Or any other class that use the IoC (Controller/Middleware/etc...
export class MyService {
constructor(private configService: ConfigService) {
this.configService.change$.on(CONFIG_EVENTS.CHANGE, configName => {
if (configName === 'my_config') {
// Do something with the new config.
}
});
}
}
Applications
The template provide a koa app instance by default, but you can define and run multiple app at the same time.
To create a new koa app, first create a new file called my_app.app.ts
and export a class:
@Application('my_app', MY_APP_PORT)
export default class MyApp extends AbstractApp {}
Now you can use your new app on a specific controller :
@Controller({ app: 'my_app' })
/*...*/
Cron
You can declare a cron job using the @Cron()
decorator.
import { Cron } from "@m-backend/core/";
@Cron({ cronTime: '* * * * * *' })
Log
This template include the winston package for app logging.
A service is provided:
/* ... imports, decorators */
export default class MyClass {
constructor(private logService: LoggerService) {}
someMethod() {
this.logService.logger.info('log with info level');
this.logService.logger.error('log with error level');
/* etc... */
}
}
Configure log output
3 type of output are supported by default:
- Console for a colorful console output
- No color console for a non-colorful console output
- File for file generation containing your app logs.
Note: The loggerService uses your
app.config.json
file for that.
{
"logger": {
"level": "silly",
"transports": [
{ "type": "console" },
{
"type": "file",
"options": {
"filename": "app.log",
"maxsize": 100000,
"maxFiles": 10,
"tailable": true,
"zippedArchive": true
}
}
]
}
}
Console
This template include the commander package to manage command line arguments and options.
How to define a command line
import { Console, Command } from "@m-backend/core";
@Console('my-console')
export default class MyConsole extends ConsoleBase {
/**
* Simple command line example.
**/
@Command('my-cmd', 'my-cmd description')
async myCmd(): void {
// ...
}
/**
* Command line example with options.
* See the commander documentation for command and option syntaxes.
**/
@Command('my-cmd2 <myValue>', 'my-cmd description', [
{ option: '-l, -lorem-ipsum [value]', description: 'Lorem ipsum dolor sit amet', default: 42 }
])
async myCmd2(myValue: string, opts: any): void {
console.log(myValue);
console.log(opts.loremIpsum);
// ...
}
}
Exemple of use:
node app.js my-console:my-cmd
node app.js my-console:my-cmd2 23 -l
Note: The console name is automatically added as a prefix to the command name to avoid name conflicts and allow a better visualization in the result of the help command.
Example
Controller
import { Context } from 'koa';
import { Joi } from '@koa-better-modules/joi-router';
import LoremMiddleware from '@app/lorem.middleware';
import { AttachMiddleware, Controller, LoggerService, Get, Post } from '@m-backend/core';
@AttachMiddleware([LoremMiddleware])
@Controller({ prefix: '/api' })
export default class LoremController {
constructor (private log: LoggerService) { } //private config: ConfigService, Inject this after fixing default export
@AttachMiddleware([LoremMiddleware])
@Get('/ipsum')
async getIpsum(ctx: Context) {
ctx.body = { message: 'test ipsum dolor sit amet' };
}
@Post('/lorem/:id', {
body: {
name: Joi.string().max(100).required(),
email: Joi.string().lowercase().email().required()
},
type: 'json'
})
async postLorem(ctx: Context) {
ctx.response.body = ctx.request.body;
(ctx.response.body as any).id = ctx.request.params.id;
}
}
Cron
import { CronJob } from "cron";
import { Cron, OnTick, LoggerService } from "@m-backend/core";
@Cron({ cronTime: '* * * * * *' })
export default class SampleCron implements OnInit, OnTick, OnComplete {
/**
* The cron job instance.
* /!\ Not available in the constructor. Use the OnInit interface if you want to start the cron manually.
*/
job: CronJob | undefined;
onInit(): void {
/* ... init code here. */
this.job.start(); // Or use the start property in the decorator options.
setTimeout(() => {
// Stop the job and call the onComplete callback.
this.job?.stop();
}, 5000)
}
onTick(): void {
/* ... */
}
onComplete(): void {
/* ... */
}
}