npm package: @m-backend/core
Description: This library was generated with [Nx](https://nx.dev).
Latest version: 1.2.3

This library was generated with [Nx](https://nx.dev).

# Boilerplate code for M's NodeJS servers.

**Table of Content**

- [Boilerplate code for M's NodeJS servers.](#boilerplate-code-for-ms-nodejs-servers)
	- [Install](#install)
	- [Overview](#overview)
	- [Dependency Injection](#dependency-injection)
	- [Services](#services)
	- [Middleware](#middleware)
	- [Routing](#routing)
		- [Controller Options](#controller-options)
		- [Attach middlewares](#attach-middlewares)
	- [Configurations](#configurations)
		- [Loading a configuration file](#loading-a-configuration-file)
		- [Loading a configuration folder](#loading-a-configuration-folder)
		- [Get a configuration](#get-a-configuration)
		- [Listen to config changes](#listen-to-config-changes)
	- [Applications](#applications)
	- [Cron](#cron)
	- [Log](#log)
		- [Configure log output](#configure-log-output)
	- [Console](#console)
		- [How to define a command line](#how-to-define-a-command-line)
	- [Example](#example)
		- [Controller](#controller)
		- [Cron](#cron-1)



## 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:

```typescript
@Service()
export class MyService {}
```

And use it in other services or controllers:

```typescript
@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.

```typescript
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:

```typescript
// 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:

```typescript
@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:

```typescript
@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:

```typescript
@AttachMiddleware([/* a list of middleware */])
@Controller()
export default class MyController {}
```

> **Note:** Middlewares attached to a controller will be called for each controller routes

```typescript
@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:

```typescript
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`:

```typescript
@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

```typescript
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 :

```json
{
    "whitelist": [".config.json", ".md"],
    "blacklist": ["example.config.json", ".sql"]
}
```

**Note :** 
> The function `loadConfigFolder` calls `loadConfigFile` 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:
```typescript
import { config } from '@m-backend';

config.my_config
```
Or using the config service
```typescript
@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:
```typescript
@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:

```typescript
@Application('my_app', MY_APP_PORT)
export default class MyApp extends AbstractApp {}

```

Now you can use your new app on a specific controller :

```typescript
@Controller({ app: 'my_app' })
/*...*/
```

## Cron

You can declare a cron job using the `@Cron()` decorator.

```typescript
import { Cron } from "@m-backend/core/";

@Cron({ cronTime: '* * * * * *' })
```

## Log

This template include the winston package for app logging.

A service is provided:

```typescript
/* ... 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

5 type of output are supported by default:

* console for a colorful console output
* console-no-color for a non-colorful console output
* file for file generation containing your app logs.
* daily-rotate-file for file with daily rotation
* loki for sending logs to grafana-loki

> **Note:** The loggerService uses your `app.config.json`  file for that.

```json
{
	"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.

[Commander doc](https://www.npmjs.com/package/commander).

### How to define a command line

```typescript
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

```typescript
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

```typescript
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 {
		/* ... */
	}
}
```