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

decorator-koa-router

v1.0.9

Published

Package to declare koa controllers via decorators

Downloads

7

Readme

Decorator koa router

Usage

This library streamlines the declaration of routes, allowing an agile development of the APIs.

The integration has been done under the official package of the Koa team: https://www.npmjs.com/package/@koa/router

To use this library it is necessary to work with a dependency container.

Examples have been made with the InversifyJS and node-dependency-injection packages.

The examples are in this repository: test-koa-router

You can integrate any other dependency injector as long as it complies with the "IocAdapter" interface

Example with InversifyJS

Server.ts

import 'reflect-metadata';
import Koa from 'koa';
import { join } from 'path';
import { InversifyIocAdapter } from './InversifyIocAdapter';
import { RouterBuilder } from 'decorator-koa-router';

export class Server {
  public static async load(): Promise<void> {
    const port = 3000;
    const koaServer = new Koa();
    
    const controllersPath = join(__dirname, './Controllers/*.ts');// The path where controllers are
    const iocAdapter = new InversifyIocAdapter();
    const router = await RouterBuilder('/api', iocAdapter, controllersPath);
    
    koaServer.use(router);
    koaServer.listen(port);
    console.info(`Server running at port ${port}`);
  }
}
Server.load();

InversifyIocAdapter

import { IocAdapter } from '../../IocAdapter';
import { Container } from 'inversify';
import { AdapterTypes } from './AdapterTypes';
import { ProductContainer } from './Containers/ProductContainer';
import { UserContainer } from './Containers/UserContainer';

export class InversifyIocAdapter implements IocAdapter {
  private _container: Container = new Container();

  constructor() {
    this.inject(ProductContainer.container());
    this.inject(UserContainer.container());
  }

  public get<T>(className: string): T {
    return this.container().get<T>(className);
  }

  private container(): Container {
    return this._container;
  }

  private inject(dependencies: AdapterTypes) {
    const { controllers, services } = dependencies;
    if (controllers) this.bindControllers(controllers);
    if (services) this.bindServices(services);
  }

  private bindControllers(controllers: AdapterTypes['controllers']) {
    if (controllers) {
      for (const controller of controllers) {
        this.container().bind(controller.name).to(controller).inRequestScope();
      }
    }
  }

  private bindServices(services: AdapterTypes['services']) {
    if (services) {
      for (const service of services) {
        this.container().bind(service.name).to(service).inRequestScope();
      }
    }
  }
}

ProductContainer

import { AdapterTypes } from '../AdapterTypes';
import { ProductController } from '../Controllers/ProductController';
import { ProductFinder } from '../Services/Product/ProductFinder';
import { ProductCreator } from '../Services/Product/ProductCreator';
import { ProductModifier } from '../Services/Product/ProductModifier';
import { ProductRemover } from '../Services/Product/ProductRemover';

export class ProductContainer {
  public static container(): AdapterTypes {
    return {
      controllers: [ProductController],
      services: [ProductFinder, ProductCreator, ProductModifier, ProductRemover],
    };
  }
}

ProductController

import { Context, Response } from 'koa';
import { ProductFinder } from '../Services/Product/ProductFinder';
import { controller, httpDelete, httpGet, httpPost, httpPut } from 'decorator-koa-router';
import { inject, injectable } from 'inversify';
import { ProductCreator } from '../Services/Product/ProductCreator';
import { ProductModifier } from '../Services/Product/ProductModifier';
import { ProductRemover } from '../Services/Product/ProductRemover';

@controller('/product')
@injectable()
export class ProductController {
  constructor(
    @inject('ProductFinder') private readonly productFinder: ProductFinder,
    @inject('ProductCreator') private readonly productCreator: ProductCreator,
    @inject('ProductModifier') private readonly productModifier: ProductModifier,
    @inject('ProductRemover') private readonly productRemover: ProductRemover
  ) {}

  @httpGet('/')
  public async listAll({ response }: Context): Promise<Response> {
    const product = await this.productFinder.run();
    response.status = 200;
    response.body = product;
    return response;
  }

  @httpPost('/')
  public async create({ response }: Context): Promise<Response> {
    await this.productCreator.run();
    response.status = 201;
    return response;
  }

  @httpPut('/:productId')
  public async modify({ response }: Context): Promise<Response> {
    await this.productModifier.run();
    response.status = 204;
    return response;
  }

  @httpDelete('/:productId')
  public async remove({ response }: Context): Promise<Response> {
    await this.productRemover.run();
    response.status = 200;
    return response;
  }
}

ProductFinder.ts (service)

import { injectable } from 'inversify';

@injectable()
export class ProductFinder {
  public async run(): Promise<Record<string, string | number>> {
    return {
      id: 1,
      name: 'test',
    };
  }
}

Example with node-dependency-injection

server.ts

import 'reflect-metadata';
import Koa from 'koa';
import { join } from 'path';
import { RouterBuilder } from '../../RouterBuilder';
import { RouterBuilder } from 'decorator-koa-router';

export class Server {
  public static async load(): Promise<void> {
    const port = 3000;
    const koaServer = new Koa();
    const controllersPath = join(__dirname, './Controllers/*.ts');
    const iocAdapter = new NodeDependencyInjectionIocAdapter();
    const router = await RouterBuilder('/api', iocAdapter, controllersPath);
    koaServer.use(router);
    koaServer.listen(port);
    console.info(`Server running at port ${port}`);
  }
}
Server.load();

NodeDependencyInjectionIocAdapter.ts

import { IocAdapter } from '../../IocAdapter';
import { join } from 'path';
import { ContainerBuilder, YamlFileLoader } from 'node-dependency-injection';

export class NodeDependencyInjectionIocAdapter implements IocAdapter {
  private readonly container: ContainerBuilder;

  constructor() {
    this.container = new ContainerBuilder();
    const loader = new YamlFileLoader(this.container);
    loader.load(join(__dirname, './Containers/Container.yaml'));
  }

  get<T>(className: string): T {
    return this.container.get<T>(className);
  }
}

ProductController.ts

import { Context, Next, Response } from 'koa';
import { ProductFinder } from '../Services/Product/ProductFinder';
import { controller, httpDelete, httpGet, httpPost, httpPut } from 'decorator-koa-router';
import { ProductCreator } from '../Services/Product/ProductCreator';
import { ProductModifier } from '../Services/Product/ProductModifier';
import { ProductRemover } from '../Services/Product/ProductRemover';

@controller('/product', (ctx: Context, next: Next) => {
  console.info('Middleware 1');
  next();
}, (ctx: Context, next: Next) => {
  console.info('Middleware 2...');
  next();
})
export class ProductController {
  constructor(
    private readonly productFinder: ProductFinder,
    private readonly productCreator: ProductCreator,
    private readonly productModifier: ProductModifier,
    private readonly productRemover: ProductRemover
  ) {
  }

  @httpGet('/', (ctx: Context, next: Next) => {
    console.info('Middleware 3');
    next();
  }, (ctx: Context, next: Next) => {
    console.info('Middleware 4...');
    next();
  })
  public async listAll({response}: Context): Promise<Response> {
    const product = await this.productFinder.run();
    response.status = 200;
    response.body = product;
    return response;
  }

  @httpPost('/')
  public async create({response}: Context): Promise<Response> {
    await this.productCreator.run();
    response.status = 201;
    return response;
  }

  @httpPut('/:productId')
  public async modify({response}: Context): Promise<Response> {
    await this.productModifier.run();
    response.status = 204;
    return response;
  }

  @httpDelete('/:productId')
  public async remove({response}: Context): Promise<Response> {
    await this.productRemover.run();
    response.status = 200;
    return response;
  }
}

ProductFinder.ts (service)

export class ProductFinder {
  public async run(): Promise<Record<string, string | number>> {
    return {
      id: 1,
      name: 'test',
    };
  }
}

Decorators reference

| Signature | Example | Description | | ------------------------------------ | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | @controller(controllerRoute: string, ...middlewares) | @controller("/product") class ProductController | class defined as controller, the paths of the methods that contain this class will be concatenated. | @httpGet(methodRoute: string, ...middlewares) | @httpGet("/") async getProduct() | This decorator defines a route of type GET
| @httpPost(methodRoute: string, ...middlewares) | @httpPost("/") async createProduct() | This decorator defines a route of type POST
| @httpPut(methodRoute: string, ...middlewares) | @httpPut("/:productId") async updateProduct() | This decorator defines a route of type PUT
| @httpDelete(methodRoute: string, ...middlewares) | @httpDelete("/:productId") async removeProduct() | This decorator defines a route of type DELETE