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

express-dependency-injection

v1.5.0

Published

a dependency injector for express using decorators

Downloads

11

Readme

Build Status Coverage Status

express-dependency-injection

Build complete express web server in Typescript, using decorators to define your routers, routes and middlewares, giving you the full flexibility of dependency injection for test, and the performance optimisation of singleton class injection thanks to a custom container.

Getting Started

Those instructions while help you get up and running with the express-dependency-injection module on your local machine

Prerequisites

Run Dependencies

  • node: ^8.11
  • npm: ^5.6
  • npm modules :
    • @types/express: ^4.16.1
    • express: ^4.16.4
    • reflect-metadata: ^0.1.13
    • typescript: ^3.3.3333

Test Dependencies

test dependencies should be added with the --save-dev flag using npm

ts-node and mocha have to be installed globally using the -g flag using npm !

  • run dependencies
  • chai: ^4.2.0
  • chai-spies: ^1.0.0
  • mocha: ^6.0.2
  • ts-node: ^8.0.2

Installing

From Scratch

To start using the express-dependency-injection module, simply initialise an empty typescript npm project as follow :

npm init #follow instructions to initialise your pakage.json
tsc --init #generate a generic tsconfig.json typescript transpiler configuration file
npm i --save express-dependency-injection # install the acutal module

turn in experimental decorators and decorator metadata in your tsconfig.json file :

     "experimentalDecorators": true,
     "emitDecoratorMetadata": true 

start having fun !

Existing Project

Run the npm i --save express-dependency-injection command turn in experimental decorators and decorator metadata in your tsconfig.json file :

     "experimentalDecorators": true,
     "emitDecoratorMetadata": true 

you're ready to go !

Running Tests

This package tests cover:

  • container functionnalities : registering, lazy-loading, retrieval, unicity of registered dependencies
  • decorators functionnalities : routers, routes, middleware registering and automatic hooking to the generated express underlying object

to run test, clone the associated github repository, cd your way to the root project and do as following :

make sure you have the correct versions of ts-node and mocha installed globally on your machine !

npm install # install all dependencies
npm run-script test # run tests, output results as text, functionnalities wise

API Documentation

Building a basic application routing

this documentation follow the learn by example strategy and should be used with the dedicated example project available at express-dependency-injection-example

start by defining your application root class and root router :

root class :

import { AbstractServer, ExServer } from "express-dependency-injection";
import { MainRouter } from "../routers/main.router";

/*
ExServer define the server entry point, check if extends AbstractServer,
and require a main router to be defined
AbstractServer provide the listen method to start the underlying express server
*/
@ExServer({
    main: MainRouter
})
export class App extends AbstractServer {

} 

main router :

import { AbstractRouter, ExRouter } from "express-dependency-injection";
import { Router1 } from "./router1.router";
import { Router2 } from "./router2.router";
import { Example1Middleware } from "../middlewares/example1.middleware";

/*
Main router, showcasing the ability to bind branch routers,
can also declare actual routes within it's body
if you need a global middleware, declare it in this middleware array 
*/
@ExRouter({
    path: '/',
    routers: [
        Router1,
        Router2
    ],
    middlewares: [
        Example1Middleware
    ]
})
export class MainRouter extends AbstractRouter {


}

Define subrouters, as follow :

router2, showcasing post and get methods :

import { Request, Response } from "express";
import { AbstractRouter, ExRouter, ExRoute, Inject } from "express-dependency-injection";
import { HttpVerbs } from "express-dependency-injection/dist/enums/http.verbs.enum";
import { ExampleRepository } from "../repositories/example.repository";
import { from } from "rxjs"; 
import { ExampleModel } from "../models/example.model";
import { Example2Middleware } from "../middlewares/example2.middleware";

/*
This Example router showcase how to integrates the module with mongoose through
the typegoose Typescript annotated API.
limited Here to GET and POST verbs, but all HTTP verbs are availables.
*/
@ExRouter({
    path: '/example'
})
export class Router2 extends AbstractRouter {

    @Inject(ExampleRepository)
    private repo: ExampleRepository;

    @ExRoute({
        path: "/get/:limit", //precise arguments with express syntax
        verb: HttpVerbs.GET
    })
    public findLasts(req: Request, res: Response, args: {params: {limit: string}, body: null}) {

        if(!! args.params.limit) {

            from(this.repo.getModel().findByProperty1(args.params.limit))
            .subscribe(
                data => res.json(data),
                err => res.status(500).send(err)
            )
        } else {

            res.status(400).send('bad parameters sent');
        }
    }
    @ExRoute({
        path: "/saveAll",
        verb: HttpVerbs.POST,
        middlewares: [
            Example2Middleware
        ]
    })
    public saveAll(req: Request, res: Response, args: {params: null, body: Array<ExampleModel>}) {

        const validated = args.body.reduce((acc, model) => {
            
            return acc && model.property1 !== undefined
            && model.property2 !== undefined
        
        }, true);
        if(validated) {

            from(this.repo.getModel().insertMany(args.body))
            .subscribe(
                data => res.json(data),
                err => res.status(500).send(err)
            )
        } else {

            res.status(400).send('bad request body sent');
        }
    }
}

http verbs compatible with a body will be injected the parsed body (require body-parser as middleware on the route) as args.body, wich you can type as the expected object you should receive. any arguments passed through uri while be available on args.params and can be typed just like body is. A futur version will allow the apposition of guard like Classes on the route decorator parameters list.


router1, showcasing other HTTP verbs are available :

import { AbstractRouter, ExRouter, ExRoute, HttpVerbs } from "express-dependency-injection";
import { SubRouter } from "./sub-router.router";
import { Example1Middleware } from "../middlewares/example1.middleware";
import { Request, Response } from "express";

/*
show some other HTTP verbs available
*/
@ExRouter({
    path: '/otherexample',
    routers: [
        SubRouter
    ],
    middlewares: [
        Example1Middleware
    ]
})
export class Router1 extends AbstractRouter {

    @ExRoute({
        path: "/delete/:id",
        verb: HttpVerbs.DELETE
    })
    public delete(req: Request, res: Response, args: {params: {id: string}, body: null}) {

        res.send(`called with DELETE verb with ${args.params.id} param`);
    }
}

Define Middlewares :

for project specific middleware logic, just implements the run method :

import { AbstractMiddleware, ExMiddleware } from "express-dependency-injection";
import { Request, Response } from "express";

/*
ExMiddleware Class decorator works as Service one, but check class inheritance from
AbstractMiddleware at runtime, throw error if not
Abstract middleware force the implementation of run method, wich should implements
whatever logic the middlware has to perform on request or response objects
*/
@ExMiddleware()
export class Example1Middleware extends AbstractMiddleware {
    
    run(req: Request, res: Response) {

        //do something with request or response object !
        console.log('middleware called !');
    }



}

for imported module middelwares (like body-parser), overright the handle method as follow :

import { AbstractMiddleware, ExMiddleware } from "express-dependency-injection";
import { Request, Response } from "express";
import bodyParser = require("body-parser");
import { ExpressMiddleware } from "express-dependency-injection/dist/types/function.type";

/*
ExMiddleware Class decorator works as Service one, but check class inheritance from
AbstractMiddleware at runtime, throw error if not
Abstract middleware force the implementation of run method, wich should implements
whatever logic the middlware has to perform on request or response objects
*/
@ExMiddleware()
export class Example2Middleware extends AbstractMiddleware {
    
    run(req: Request, res: Response) {
        
        //not called !
    }
    /*redefine the inherited handle function
    if you have to use an external middleware module
    (like body-parser in this example)
    */
   public handle(): ExpressMiddleware {

        return bodyParser.json();
    }
}

Define Repositories :

import { Repository, Inject, ExRepository } from "express-dependency-injection";
import { GenericRepository } from "./generic.repository";
import { ExampleModel } from "../models/example.model";

/*
ExRepository Class decorator checks if extends the Repository mixin.
Repository mixin allows to factorize common behaviours (here connection to database)
and force to implements the getModel method to retrieve
the database representation of the model
*/
@ExRepository()
export class ExampleRepository extends Repository(GenericRepository) {

    @Inject(ExampleModel)
    private exampleModel: ExampleModel

    getModel() {

        this.connect();
        return this.exampleModel.getModelForClass(ExampleModel);
    }
} 

here is an example of typegoose implementation inside of a repository annotated class. Warning : any injected class with the @Inject decorator MUST be annotated as service with the @Service decorator ! Here, the extended GenericRepository class leverage the connection logique to the database, and expose it's connect method to keep logic well separated.


RoadMap

  • adding guards as class to routes and router
  • providing an implementation for static assets loading

Issues, Questions, Propositions ?

Any question, bug report, or propositions on new features are more than welcome, feel free to use the report issue section of npm or github repository. You can also contact use at [email protected]