JSPM

  • Created
  • Published
  • Downloads 103
  • Score
    100M100P100Q58096F
  • License MIT

NodeJS project using ExpressJS to create an easy, secure and customisable API layer

Package Exports

  • tree-house

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (tree-house) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

npm version Dependencies Build Status Coverage Status

Treehouse

NodeJS project using ExpressJS to create an easy, secure and customisable API layer

Installation

npm install tree-house

Usage

All example code is written in ES6, but usage with ES5 will also properly work with Promise support.

Configuration

Key Description Default value
port The application will run on this port 5000
bodyLimit Limit used for body-parser 10mb
apiKey An api key mapped to process.env to use throughout the application ga9ul2!MN36nyh64z4d5SC70jS
basePath The base path used to access all api routes `process.env.BASE_PATH
https Configuration if you wish to start up an https server false

#####You can create a configuration object with own preferences and set this later on. See below in Basic Setup.

const myOwnConfiguration = {
    port: 3000,
    bodyLimit: '5mb',
    apiKey: 'myOwnApiKey',
    basePath: process.env.BASE_PATH || '/api',
}

#####HTTPS configuration (optional)

myOwnConfiguration = { 
  // Other configuration ...
  
  https: {
      certificate: 'path_to_folder/test-ssl.cert',
     privateKey: 'path_to_folder/test-ssl.key',
      port: 5001
  }
}

###Getting started ####Import the module from tree-house and create a new instance.

import { TreeHouse } from 'tree-house';
const application = new TreeHouse();

If you wish to overwrite the default application configuration, there are two options:

const application = new TreeHouse(myOwnConfiguration);

or

const application = new TreeHouse();
application.setConfiguration(myOwnConfiguration);

Controllers

All controllers need to extend from BaseController which only provides one function: execute(res, fn)

It is required to provide a function that returns a promise

import { BaseController } from 'tree-house';

export class MyController extends BaseController {
    constructor() {
        super();
        this.myService = new MyService();      // Needed to use functions from this service
    }

    getAllEmployees = (req, res) => {
        return this.execute(res, this.myService.getEmployees());
    }
}

You need to make sure all functions declared in a controller that are being used by a route are automatically bound to the context of their Controller class by using fnName = (req, res) => { logic here... }. Dont't use fn(req,res) { logic here... }.

If you don't use this specific syntax, you won't be able to reference to this as the context of the MyController class. It will have become an instance of the Route class which won't allow you to call this.execute() or even this.myService.getEmployees().

Services

All services need to extend from BaseService which provides a few custom errors with corresponding response codes you can throw at any given moment.

It is required to return a promise if you wish to invoke the function in a controller as declared above.

import { BaseService } from 'tree-house';

export class MyService extends BaseService {
    getEmployees() {
        // Get all employees from db and return them for example
        return myDb.executeQuery('SELECT * FROM EMPLOYEES');
    }
}

Policies

All policies need to extend from BasePolicy which only provides one function: setPolicy()

import { passportAuthentication } from './main'; // Instance created via module tree-house-authentication
import { BasePolicy } from 'tree-house';

export class MyPolicy extends BasePolicy {
    setPolicy() {
        return passportAuthentication.authenticate(this.req, 'jwt')
            .then((user) => {
                if (!user) throw new this.Unauthorised();
                return Object.assign(this.req, { session: { me: user } });
            });
    }
}

Routes

This is one of the most important parts of the application because all the controllers and policies need to be properly set here. You can create a route with path parameters or query parameters as if you would do in a regular expressJS application. Check the ExpressJS documentation for more information.

You need to create an array consisting of Route objects. A Route object has four parameters:

  • HTTP method
  • Path of the route (will be prepended by basePath from the configuration)
  • Function to call when the route gets triggered
  • Array of policies (middleware that gets called before the function above) - optional
import { Route } from 'tree-house';
import { MyController } from './myController';
import { MyPolicy } from './myPolicy';

const myController = new MyController();
            
const myRoutes = [
    new Route('GET', '/employees', myController.getAllEmployees, [MyPolicy])

Set routes onto the main application

application.setRoutes(myRoutes);

Authentication

All authentication needs to extend from BaseAuthentication which only requires the implementation of a function authenticate() at the moment.

You can already use some predefined authentication methods by installing the tree-house-authentication module using npm install tree-house-authentication.

Errors

All errors need to extend from BaseError. Providing own custom errors is not yet supported at the moment but is already on the roadmap for the next release.

There are a few errors already predefined which can be used in every service extending from BaseService or every policy extending from BasePolicy. You can even provide a custom message and/or code:

throw new this.BadRequest('This is a bad request', 'BAD_REQUEST');
      
throw new this.Unauthorised('You are not authorized to perform this action', 'NOT_AUTHORISED';
      
throw new this.ServerError('Something went wrong', 'SERVER_ERR');
Predefined errors:
Error Default Message Default code Status
BadRequest This call is not valid, and thereby a bad request. BAD_REQUEST 400
Unauthorised You are not authorised to make this call. NOT_AUTHORISED 401
ServerError Something went wrong. Our technicians are working on it! SERVER_ERR 500

Fire up them engines!

Start up the expressJS server after you've configured everything:

application.fireUpEngines()

Use the instance throughout the application

It's best to use the same instance you've created in your main file throughout your whole application. The best way to achieve this is exporting the instance via module.exports:

module.exports.application = application;

This will allow you to use the same instance in all of your files via proper ES6 imports:

import { application } from './main';

// Call all functions and variables via this instance
    application.getConfiguration();
    ...

Examples

Example code is provided in the examples folder.

Tests

You can run npm test or npm run cover to run all tests and get a coverage report.

BUGS

When you find issues, please report them:

Be sure to include all of the output from the npm command that didn't work as expected. The npm-debug.log file is also helpful to provide.

Contributing

In lieu of a formal style guide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code.