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

rafter

v0.8.75

Published

A slightly opinionated JS framework that makes building apps easy and testable

Downloads

8,082

Readme

Rafter Framework

Rafter is a lightweight, slightly opinionated Javascript framework for rapid development of web applications.

Rafter:

  • is built on top of Expressjs.
  • eliminates the tedious wiring of routes, middleware and services.
  • allows decoupling of services by utilizing dependency injection via an autoloading service container.
  • is flexible and testable.

Install

npm install --save rafter

OR

yarn add rafter

Getting started

The following configuration files are autoloaded during the Rafter service starting:

  • config.ts: a general application or module config.
  • middleware.js: registers services as middleware.
  • routes.ts: links controller services to route definitions.
  • pre-start-hooks.js: loads defined services before Rafter has started the server.

The Rafter autoloader will look for all of these files recursively throughout your project. This allows you to modularize your project rather than defining all your config in one place.

Config

The config file (config.ts) is a place to define all your application style config.

export default {
  db: {
    connectionUrl: 'mongodb://localhost:27000/rafter' || process.env.NODE_DB_CONNECTION,
  },
  server: {
    port: 3000,
  },
  example: {
    message: `Hello Mars`,
  },
};

Middleware

The middleware file (middleware.js) exports an array of service name references which will be loaded/registered in the order in which they were defined. eg.

export default (): IMiddlewares => new Set<IMiddlewareConfig>([`corsMiddleware`, `authenticationMiddleware`]);

Note; the middleware must be registered in the .services.ts config.

Routes

The routes file (routes.ts) exports an array of objects which define the http method, route, controller and action. eg.

export default (): IRoutes =>
  new Set<IRouteConfig>([
    {
      endpoint: `/`,
      controller: `exampleController`,
      action: `index`,
      method: `get`,
    },
  ]);

This would call exampleController.index(req, res) when the route GET / is hit.

Pre start hooks

The routes file (pre-start-hooks.js) exports an array of service references that will be executed before Rafter has started, in the order in which they were defined. This is useful for instantiating DB connections, logging etc.

export default (): IPreStartHooks => new Set<IPreStartHookConfig>([`connectDbService`]);

An example of the connectDbService would be:

export default (dbDao, logger) => {
  return async () => {
    logger.info(`Connecting to the database`);
    return dbDao.connect();
  };
};

Rafter instantiation

Along with the aforementioned configs, all that is required to run Rafter is the following:

import rafter from 'rafter';

const run = async () => {
  const rafterServer = rafter();
  await rafterServer.start();
};

run();

Once start() is called, Rafter will:

  1. Scan through all your directories looking for config files.
  2. Autoload all your services into the service container.
  3. Run all the pre-start-hooks.
  4. Apply all the middleware.
  5. Register all the routes.
  6. Start the server.

To see an example project, visit the skeleton rafter app repository.

Going deeper

Rafter is slightly opinionated; which means we have outlined specific ways of doing some things. Not as much as say, Sails or Ruby on Rails, but just enough to provide a simple and fast foundation for your project.

The foundations of the Rafter framework are:

  • Dependency injection
  • Autoloading services
  • Configuration

Dependency injection

With the advent of RequireJs, dependency injection (DI) had largely been thrown by the way side in favor of requiring / importing all your dependencies in Node. This meant that your dependencies were hard coded in each file, resulting in code that was not easily unit testable, nor replicable without rewrites.

eg.

With RequireJs

import mongoose from 'mongoose';

const connect = async (connectionUrl) => {
  await mongoose.connect(connectionUrl);
};

const find = async (query) => {
  await mongoose.find(query);
};

export { connect };

With DI

export default class DbDao {
  constructor(db) {
    this._db = db;
  }

  async connect(connectionUrl) {
    return this._db.connect(connectionUrl);
  }

  async find(query) {
    return this._db.find(query);
  }
}

As you can see with DI, we can substitute any DB service rather than being stuck with mongoose. This insulates services which use a data store from caring what particular store it is. eg. If our DB becomes slow, we can simply substitute a CacheDao instead, and no other services would have to change.