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

1.1.0 • Public • Published

Thresh

npm version Coverage Status Build Status License: MIT

Decorative implementation of Express with TypeScript and dependency injection

Table of Contents

Installation

Install with npm

npm install thresh --save

Or yarn

yarn add thresh

Usage

Basic

The most basic implementation with no dependencies or middleware. This is an Express app listening on port 3000 for GET requests on '/hello'. As a note /hello could also be a RegExp, Express path expression or an array containing any combination of those.

import { Thresh, Route, Request, Response } from 'thresh';
 
@Thresh({ express: [3000] })
class ThreshApplication {
  @Route('/hello')
  helloWorld(req: Request, res: Response) {
    res.json({ hello: 'world' });
  }
}
 
new ThreshApplication();

Services

Services are created as singletons and are available and provided for in the decorator @Thresh. They are immediately available in the constructor of the class that provided them, as well as the constructor of all nested routers.

The ExpressService and RootService are available to the root application and every nested router. ExpressService notably contains the app variable which is the Express Application/Router. RootService is the ExpressService for the root application.

import {
  Thresh,
  Route,
  Request,
  Response,
  ExpressService,
  RootService
} from 'thresh';
 
class FooService {
  public foo: string = 'bar';
}
 
@Thresh({
  services: [FooService],
  express: [3000]
})
class ThreshApplication {
  constructor(
    private fs: FooService,
    private rs: RootService,
    private es: ExpressService
  ) {
    console.log(this.rs === this.es); // true
    this.es.app.use('/', myAwesomeMiddleware());
  }
 
  @Route('/hello') // GET http://localhost:3000/hello
  helloWorld(req: Request, res: Response) {
    res.json({ foo: this.fs.foo }); // { foo: 'bar' }
  }
}
 
new ThreshApplication();

Routers

Routers are essentially identical to Apps and are also declared with @Thresh. They will inherit services from their parents.

import { Thresh, Route, Request, Response } from 'thresh';
 
class FooService {
  public foo: string = 'bar';
}
 
@Thresh()
class ThreshRouter {
  constructor(fs: FooService) {}
 
  @Route('/hello') // GET http://localhost:3000/foo/hello
  helloWorld(req: Request, res: Response) {
    res.json({ foo: this.fs.foo }); // { foo: 'bar' }
  }
}
 
@Thresh({
  routers: [['/foo', ThreshRouter]]
  services: [FooService],
  express: [3000]
})
class ThreshApplication {}
 
new ThreshApplication();

Router Scope

Services provided in routers will receive their own singletons isolated from the router's parents.

import { Thresh, Route, Request, Response } from 'thresh';
 
class FooService {
  public foo: string = 'bar';
}
 
@Thresh({
  services: [FooService]
})
class ThreshRouter {
  constructor(fs: FooService) {
    console.log(this.fs.foo) // 'bar'
    this.fs.foo = 'banana';
  }
 
  @Route('/hello') // GET http://localhost:3000/foo/hello
  helloWorld(req: Request, res: Response) {
    res.json({ foo: this.fs.foo }); // { foo: 'banana' }
  }
}
 
@Thresh({
  routers: [['/foo', ThreshRouter]]
  services: [FooService],
  express: [3000]
})
class ThreshApplication {
  constructor(fs: FooService) {
    this.fs.foo = 'apple';
  }
 
  @Route('/hello') // GET http://localhost:3000/hello
  helloWorld(req: Request, res: Response) {
    res.json({ foo: this.fs.foo }); // { foo: 'apple' }
  }
}
 
new ThreshApplication();

Middleware

Middleware like routes, can also be chained by passing no parameters to the class method and returning an array of middleware.

import {
  Thresh,
  Route,
  Middleware,
  Request,
  Response,
  NextFunction
} from 'thresh';
 
@Thresh({ express: [3000] })
class ThreshApplication {
  @Middleware('/')
  addHero(req: Request, res: Response, next: NextFunction) {
    req.query.name = 'Peter Parker';
    next();
  }
 
  @Middleware('/villian')
  addVillian() {
    // ^-- No Parameters
    return [
      function(req: Request, res: Response, next: NextFunction) {
        console.log(`You're done ${req.query.name}!`);
        next();
      },
      function(req: Request, res: Response, next: NextFunction) {
        req.query.name = 'Doctor Octavius';
        next();
      }
    ];
  }
 
  @Route('/hello')
  hello(req: Request, res: Response) {
    res.send(`Hello, ${req.query.name}!`); // Hello, Peter Parker!
  }
 
  @Route('/villian')
  getVillian(req: Request, res: Response) {
    res.send(`Oh no, it's ${req.query.name}!`);
    // Oh no, it's Doctor Octavius
  }
}
 
new ThreshApplication();

Params

Parameter interceptors are assigned via the @Param decorator. Pass in a string with the parameter you want to intercept and act on.

import { Thresh, Route, Param, Request, Response, NextFunction } from 'thresh';
 
@Thresh({ express: [3000] })
class ThreshApplication {
  @Param('name')
  addName(req: Request, res: Response, next: NextFunction, name: any) {
    req.query.name = name + 'awesome';
    next();
  }
 
  @Route('/hello/:name')
  hello(req: Request, res: Response) {
    res.send(`Hello, ${req.query.name}!`); //Hello [name]awesome!
  }
}
 
new ThreshApplication();

HTTP Methods

All HTTP verbs that Express supports, Thresh does as well. They can either be typed out as a string or imported by the MethodTypes constant. If @Method is not defined, the route will default to GET.

import {
  Thresh,
  Route,
  Request,
  Response,
  NextFunction,
  Method,
  MethodTypes
} from 'thresh';
 
@Thresh({ express: [3000] })
class ThreshApplication {
  @Route('/hello')
  @Method(MethodTypes.Post) // POST: http://localhost:3000/hello
  helloWorld(req: Request, res: Response) {
    res.send(`Hello, world!`); // Hello, world!
  }
 
  @Route('/helloall')
  @Method('put') // PUT: http://localhost:3000/helloall
  helloAll(req: Request, res: Response) {
    res.send(`Hello, everyone!`); // Hello, everyone!
  }
}
 
new ThreshApplication();

Hooks

Several lifecycle hooks are available to modify the App/Router creation.

In Order:

  • onInit: Express and Services initialized, but no routes/routers/middleware added yet
  • afterInit: Just after the child Routers, routes and middleware have been added
  • onStart: Just before Express.listen is called, only called on the root application
  • afterStart: Just after Express.listen is called, only called on the root application
import {
  Thresh,
  Route,
  Request,
  Response,
  App,
  Injector,
  afterInit
} from 'thresh';
 
@Thresh({ express: [3000] })
class ThreshApplication implements afterInit {
  @Route('/hello')
  helloWorld(req: Request, res: Response) {
    res.send(`Hello, world!`);
  }
 
  afterInit(app: App, services: Injector) {
    console.log(app._router.stack); // List all routes
    app.use('/public', express.static('public')); // Serve public files
  }
}
 
new ThreshApplication();

Route/Middleware Ordering

If compiled to ES2015+ (ES6+) Routes and Middleware are applied exactly how they appear in the class from top to bottom. Compiling further back than that doesn't guarantee object property ordering ECMA-262. To overcome this a static $order: string[] can be provided with the exact order to apply routes/middleware in. Any @Routes or @Middleware not declared in $order will be applied after those in $order.

import { Thresh, Route, Request, Response, NextFunction } from 'thresh';
 
@Thresh({ express: [3000] })
class ThreshApplication implements afterInit {
  static $order = ['appliedFirst', 'appliedLast'];
 
  @Route('/hello')
  appliedLast(req: Request, res: Response) {
    res.send(`Hello, ${req.query.name}!`);
    // With $order: Hello, Peter Parker!
    // Without $order: Hello, undefined
    //    This is because appliedLast is applied first as
    //    it comes first in the class definition
  }
 
  @Middleware('/')
  appliedFirst(req: Request, res: Response, next: NextFunction) {
    req.query.name = 'Peter Parker';
    next();
  }
}
 
new ThreshApplication();

Still in Development

  • Providing Services within Services

Package Sidebar

Install

npm i thresh

Weekly Downloads

20

Version

1.1.0

License

MIT

Unpacked Size

13.6 kB

Total Files

5

Last publish

Collaborators

  • radbradd