npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@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

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 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:

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.

Commander doc.

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 {
		/* ... */
	}
}