graublau
TypeScript icon, indicating that this package has built-in type declarations

0.3.12 • Public • Published

Travis Codecov NPM

graublau

An object-oriented, loosely-coupled, typescript framework for creating REST applications, build on top of express, inversify, and winston.

Installation

via npm: npm install graublau

via yarn: yarn add graublau

Usage

This section will guide you through the creation of a graublau application. As an example we will build a simple service that returns some customer information. We will call our application CustomerService.

Creating a REST resource

We start by setting up our REST resources. For now we will only use one, the customer resource.

import {Module} from "graublau";
import {ResourceInterface} from "graublau";
import {inject} from "inversify";
import {provide} from "inversify-binding-decorators";

@provide(CustomerResource)
export class CustomerResource implements ResourceInterface {

    private readonly router: Router;

    constructor(@inject(Module.Types.Express) express: any) {
        this.router = express.Router();
    }

    public getRoutes(): Router {
        this.router.get("/", this.getCustomers);
        return this.router;
    }
    
    private getCustomers(request: Request, response: Response): Customer[] {
        ...
    }
}

The first thing to note here is that we use inversify to get an instance of the express module which has been bound by the Module class of graublau. While you can also load express directly we'd recommend making use of dependency injection which makes it easier to create a unit test for the resource.

All routes have to be defined in the getRoutes(): express.Router method. We assume we only have one route: GET /customers and implement the functionality in a private method.

The class is annotated with @provide(CustomerResource), using inversify's binding decorators, avoiding manual binding. All manual binding have to be performed in the Module.

Setting up the Module

The module is responsible for all dependency injection bindings that are not convered by @provide. It also provides the path to the service config file.

import {TYPES} from "./Types";
import {MongoDBStorage} from "./MongoDBStorage";
import {StorageInterface} from "./StorageInterface";
import {Module} from "graublau";
import {Container} from "inversify";

export class CustomerServiceModule extends Module {

    protected bind(container: Container): void {
        const storage: StorageInterface = container.get(MongoDBStorage);
        container.bind(TYPES.Storage).toConstantValue(storage);
    }
}

The bind() can be used for setting up DIC bindings using inversify's container class. In this example we bind the concrete implementation of MongoDBStorage to the StorageInterface. All classes depending on the interface will hence get the instance of MongoDBStorage.

Initializing the config file

The service config file is a JSON file that holds basic information for a graublau application, as well as all the values you need for your business logic. It is required to have port setting to set up the application port.

{
  "application": {
    "port": 5000
  }
}

Additionally it can also be used to customize graublau's included logger (see below).

Creating the service class

The service class bootstraps the application and takes care for registering all REST resources.

import {CustomerServiceModule} from "./CustomerServiceModule";
import {CustomerResource} from "./CustomerResource";
import {Application} from "graublau";

export class CustomerService {

    private module: CustomerServiceModule;

    public constructor(module: CustomerServiceModule) {
        this.module = module;
    }

    public run(): void {
        const application: Application = this.module.getContainer().get(Application);

        application.addResource("customers", this.module.getContainer().get(CustomerResource));
        application.start();
    }
}

The service consumes the CustomerServiceModule and uses it to load all required dependencies. It then loads the graublau Application using the Module's container. All resources have to be registered with addResource(name: string, resource: ResourceInterface). We set the name to customers so when we start the service, we can call GET /customers to call the method defined in our resource.

index.ts file

All that is left to do is instantiate both module and service, and run the application in our index.ts file.

import "reflect-metadata";
import {CustomerService} from "CustomerService";

const module = new CustomerServiceModule("/path/to/config");
const service = new CustomerService(module);
service.run();

We also use this file to load the reflect-metadata module for inversify.

Logger

graublau also provides a logger that on one hand hides away some of winston's complexity and on the other hand enriches it with additional functionality.

You can automatically use the logger using dependency injection. Per default, it will log plain text to the standard output. If you specify a logger section in your config file you can customize this behavior.

"logger": {
      "minLogLevel": "info",
      "format": "plain",
      "output": "/var/log/customer_service.log"
}
  • format can be set to either "plain" (default value), or "json" for JSON logs.
  • output can be either "stdout" for logging to the console (default value), or a file name to log to a file.
  • minLogLevel specifies the minimum log level - either "debug", "info" (default value), "warn", or "error".

You can use the logger calling one of the debug(), info(), warn(), or error() methods. Each of these methods takes two arguments:

  • The first argument is either a string, or an Error object.
  • The second argument is used for specifying the log context and can be any object (including Errors).

Dependency Injection

graublau uses the inversify dependency injection framework. It is totally possible to build a graublau application without any dependency injection support if you don't have any need for that.

When creating a Module the framework internally sets up all required bindings. Most of these bindings are only relevant to the graublau framework itself, but you can re-use them if necessary.

The following symbols are pre-bound and are exposed through Module.Types:

Symbol Bound To
configPath config path
ApplicationOptions internal application options
LoggerOptions internal logger options
Express express module
ExpressApplication instance of express application
FileSystem "fs" module
ProcessEnv process.env

Background

graublau is a framework I created extracting some common classes and structures I used in a couple of applications.

Package Sidebar

Install

npm i graublau

Weekly Downloads

0

Version

0.3.12

License

MIT

Unpacked Size

49.4 kB

Total Files

46

Last publish

Collaborators

  • tobias.hornberger