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

shaman-api

v2.0.5

Published

Framework for creating RESTful APIs using Typescript (build on top of express.js).

Downloads

9

Readme

Shaman API

Shaman API makes it super easy to create a RESTful API, using Typescript. The server is built on top of ExpressJS, and leverages InversifyJS to handle dependency injection. It comes pre-built with app configuration, an HTTP request router, a service for reading / writing JSON files, a service-client abstract class for HTTP proxy requests, and a module loader (see below).

Requirements

To safely use the shaman-api library, you should first have the following tools installed on your machine:

  • Node JS (>= 17.x)

Since this project also assumes you will be using Typescript, you will also need to have Typescript (>= 5.x) either installed on your machine, or in your local project (recommended).

Quick Start Guide

In your typescript project, make 2 new files: main.ts, and health.controller.ts. Open the health.controller.ts file and paste in the following contents:

import { Request, Response, Application, Router } from "express";
import { injectable } from "inversify";
import { ShamanExpressController } from "shaman-api";

// controller must have injectable attribute
@injectable()
export class HealthController implements ShamanExpressController {

  name: string = 'health';

  configure(express: Application) {
    let router = Router();
    router.get('/', this.getStatus);
    express.use('/api/health', router);
  }

  getStatus(_req: Request, res: Response, _next: any) {
    res.json({status: 'healthy'});
  }

}

Next, open the main.ts file and paste the following contents:

import "reflect-metadata";
import { SHAMAN_API_TYPES, ShamanExpressApp, ShamanExpressController } from 'shaman-api';
import { HealthController } from './health.controller';

let bootstrap = async () => {
  const app = new ShamanExpressApp();
  // when you compose your app, you get a 'container' object to register dependencies
  let container = await app.compose();
  // The built-in 'SHAMAN_API_TYPES.ApiController' will automatically register your controller
  container.bind<ShamanExpressController>(SHAMAN_API_TYPES.ApiController).to(HealthController);
  // initialize the express.js router instance (attaches all registered controllers)
  app.configureRouter();
  // starts the API listener, if no port is provided at construction default port will be 5000
  await app.startApplication();
}

bootstrap().catch(ex => {
  console.error(ex);
  process.exit(1);
});

Your API server is now ready to go! Simply build your application (using tsc command), navigate to your output folder, then run:

node main.js

Application Configuration

The shaman-api library comes with application configuation logic built-in, all you need to do is provide a path to your JSON configuration file when constructing the ShamanExpressApp class. For example, if I have a project with the following layout:

.
|__app.config.ts
|__config.json
|__main.ts
|__health.controller.ts

To register your configuration file (config.json) simple update the 'bootstrap' command in your main.ts file to have the following ShamanExpressApp construction:

...
const app = new ShamanExpressApp({configPath: path.join(__dirname, 'config.json')});
...

NOTE: path.join requires that you import the built-in Node JS 'path' module.

Now, to use app configuration anywhere in your application, simply inject your app config (with types defined in app.config.ts) into any service that is managed by the build-in dependency injection. For example, I could update my health.controller.ts file to include my app config like so:

import { AppConfig } from './app.config.ts';
...

export class HealthController implements ShamanExpressController {

  ...

  constructor(@inject(SHAMAN_API_TYPES.AppConfig) private config: AppConfig) {}

  ...

}

Composing dependencies

Dependency injection in shaman-api is managed by InversifyJS, and the ShamanExpressApp class provides an easy mechanism to provide your own dependencies. After constructing a ShamanExpressApp instance you will need to call 'compose', which will return an InversifyJS 'Container' instance that you can use to inject controllers / services / providers / factories /etc. Anything that is added to dependency injection can reference anything else that is available in the DI container. For more information on how to manage dependency injection, please visit the InversifyJS homepage.

NOTE: It is highly recommended that you do not compose things outside of the DI container (with obvious exceptions for things like dynamic composition).

Application Modules

The shaman-api library includes a mechanism for grouping controllers / services / etc. into isolated groups, which can be helpful for a number of reasons:

  • Security (no cross polution within DI container)
  • Runtime segregation (for example, v1, v2, etc.)
  • Plugin interfaces
  • ...Plenty more

View modules included in the shaman-api library here.

The application module interface looks like this:

import { Container } from "inversify";
import { ShamanExpressController } from "./shaman-express-controller";
export declare abstract class ShamanExpressModule {
    abstract name?: string;
    abstract compose: (container: Container) => Promise<Container>;
    isolated: boolean = false;
    export?: (container: Container) => Promise<void>;
}

For example, imagine a project with the following layout:

.
|__module1
| |__module1.controller.ts
| |__module1.service.ts
| |__module1.module.ts
| |__module1.types.ts
|__app.config.ts
|__config.json
|__main.ts
|__health.controller.ts

Module definition belongs in the module1.module.ts file, and could look something like this:

import { Container } from "inversify";
import { ShamanExpressModule, SHAMAN_API_TYPES } from "shaman-api";

import { MODULE1_TYPES } from "./module1.types";
import { Module1Service } from "./module1.service";
import { Module1Controller } from "./module1.controller";

export class SampleModule extends ShamanExpressModule {
  name: string = 'module1';
  compose = (container: Container): Promise<Container> => {
    container.bind<Module1Service>(MODULE1_TYPES.Module1Service).To(Module1Service)
    container.bind<Module1Controller>(SHAMAN_API_TYPES.ApiController).to(Module1Controller);
    return Promise.resolve(container);
  }
  export = (container: Container): Promise<void> => {
    // this method is optional, as many module will be completely isolated.
    // however, if you want to expose things outside the module, this is where you can do so.
    container.bind<Module1Service>(MODULE1_TYPES.Module1Service).To(Module1Service);
  }
}

To configure this module into the larger application ecosystem, simply provide a list of modules to the "configureRouter" method invocation in your main.ts 'bootstrap' method. For example:

import { SampleModule } from './module1/module1.module';
...

let bootstrap = async () => {
  ...
  app.configureRouter([new SampleModule()]);
  ...
}

Any controller that is composed inside of a module using the "SHAMAN_API_TYPES.ApiController" type will automatically be added to your express api, making it available via HTTP requests. If you set the module's "isolated" variable to 'false' (default, no override needed) then the component will automatically get access to the parent application module (from main.ts); if you provide the value and set it to 'true', the module will get its own isolated container, and will not have access to anything in the parent container.