@brainhubeu/hadron-express

Hadron module implementing express elements

Usage no npm install needed!

<script type="module">
  import brainhubeuHadronExpress from 'https://cdn.skypack.dev/@brainhubeu/hadron-express';
</script>

README

Installation

INFO: Currently routing with hadron works only with the Express framework.

npm install @brainhubeu/hadron-express --save

More info about installation

Express integration

We need to include hadron-express package while initializing hadron.

const express = require('express');
const bodyParser = require('body-parser');

const port = process.env.PORT || 8080;
const expressApp = express();

expressApp.use(bodyParser.json());

hadron(expressApp, [require('../hadron-express')], config).then((container) => {
  expressApp.listen(port);
});

Basic routing setup

To set up routes with Hadron, we are able to include them as objects in config object under key routes.

const config = {
  routes: {
    helloWorldRoute: {
      callback: () ='Hello world !',
      methods: ['GET'],
      path: '/',
    },
  },
};

Basic, required structure of route config object includes:

  • callback - function called when request is made, returned value will be send as a response (except if you call res methods directly)
  • methods - array of HTTP methods
  • path - route path

Callback

The callback function can take route parameters as an arguments. Hadron also allows us to grab a container value easily.

routeWithParam: {
  callback: (firstParam) => `firstParam value: ${firstParam}`,
  methods: ['GET'],
  path: '/:firstParam',
}

Using this simple example, if we send a request, for example http://localhost/foobar will provide a response as below:

"firstParam value: foobar"

When you would like to implement multiple route parameters, their order as arguments in callback does not matter, argument name needs only to match parameter name.

multipleParams: {
  callback: (secondParam, firstParam) =`${firstParam} ${secondParam}`,
  methods: ['GET'],
  path: '/:firstParam/:secondParam',
}

GET request with path: http://localhost/Hello/World will result with following response:

"Hello World"

Locals (available from 2.0.0)

As a third parameter, hadron delivers locals from your response. You can inject its content in middlewares, e.g.

const route = {
  callback: (req, container, locals) => locals.testValue,
  middlewares: [
    (req, res, next) => {
      res.locals.testValue = 'I am test!';
      next();
    },
  ],
};

Retrieving items from container in callback

Callback function provides a simple way to retrieve items from container with ease. Simply set item's key as callback function's argument. Let's see an example below:

hadron(expressApp, [require('../hadron-express')], {
  routes: {
    routeWithContainerValue: {
      // sayHello argument will refer to container value
      callback: (sayHello = `hadron says: ${sayHello}`),
      methods: ['GET'],
      path: '/',
    },
  },
}).then((container) => {
  // Register value under key sayHello
  container.register('sayHello', 'Hello World');
});

After sending a request to the / path, the response will look like that:

"hadron says: Hello World"

Hadron will first look for request parameters and next if not found any, it will look for value in the container. So if you register a key foo in a container and set the route param under the same name, it will inject param's value into callback's argument foo.

container.register('foo', 'container');
exampleRoute: {
  callback: (foo) =`foo value: ${foo}`,
  methods: ['GET'],
  path: '/:foo',
},

Response for GET request /param will look like this:

"foo value: param"

Middlewares

Note: Currently middlewares only refer to express.

Routing with Hadron provides a middleware support. You need to pass array with middleware functions to a middleware key in route config. For example:

middlewareExample: {
  callback: () => {
    console.log('Callback function');
  },
  methods: ['GET'],
  middleware: [
    (req, res, next) => {
      console.log(`First middleware`);
      next();
    },
    (req, res, next) => {
      console.log(`Second middleware`);
      next();
    },
  ],
  path: '/',
},

GET request to / will log to the console following:

First middleware
Second middleware
Callback function

Middlewares take three arguments: request, response and next. First two are objects and third one - function which executed continues request flow.

You can read more about middlewares in express guide

Routes nesting (available from 2.0.0)

In case of more complicated routing, hadron-express offers possibility to nest routes. That way, You can specify bunch of route properties, that all child routes will inherit.

Route properties that can be inherited:

  • path (will add parent's path beforehand new path),
  • middlewares
  • methods,
const nestedRoute = {
  middleware: [myTestMiddleware],
  method: ['GET'],
  path: '/test',
  routes: {
    route1: {
      // path here is going to be /test/test1/, it's going to have 'GET' method on default and middleware myTestMiddleware will be called before
      path: '/test1',
      callback: () => 'It works! Trust me...',
    },
    route1: {
      // path here is going to be /test/test2/, it's going to have 'GET' and 'POST' methods on default and middlewares myCustomMiddleware and myTestMiddleware will be called before
      path: '/test2',
      method: ['POST'],
      middleware: [myCustomMiddleware],
      callback: () => 'It works! Trust me...',
    },
  },
};

If You would like to override parent property, just define new one with $ sign before, e.g. $middlewares, $path, $method.

const nestedRoute = {
  middleware: [myTestMiddleware],
  method: ['GET'],
  path: '/test',
  routes: {
    route1: {
      // path here is going to be /test1/, it's going to have 'GET' method on default and middleware myTestMiddleware will be called before
      $path: '/test1',
      callback: () => 'It works! Trust me...',
    },
    route1: {
      // path here is going to be /test/test2/, it's going to have 'GET' and 'POST' methods on default and no middlewares
      path: '/test2',
      method: ['POST'],
      $middleware: [],
      callback: () => 'It works! Trust me...',
    },
  },
};

You can define nested route endlessly, all of them will inherit properties of it's parent and other ancestors (of course if they were not overwritten with $ sign).

const nestedRoute = {
  middleware: [myTestMiddleware],
  method: ['GET'],
  path: '/test',
  routes: {
    route1: {
      // path here is going to be /test/test2/, it's going to have 'GET' and 'POST' methods on default and no middlewares
      path: '/test2',
      method: ['POST'],
      $middleware: [],
      callback: () => 'It works! Trust me...',
      routes: {
        // path here is going to be /test/test2/deep/, it's going to have 'GET' and 'POST' methods on default and no middlewares
        deepRoute1: {
          path: 'deep',
          callback: () => 'The Dwarves delved too greedily and too deep',
        },
      },
    },
  },
};