hors
Version:
Node.js API framework
135 lines (134 loc) • 6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const inversify_1 = require("inversify");
const express = require("express");
const uuid = require("uuid");
require("reflect-metadata");
const metadata = require("./metadata");
const simple_event_1 = require("./simple-event");
const transaction_1 = require("./transaction/transaction");
const transaction_utils_1 = require("./transaction/transaction-utils");
class HorsServer {
constructor() {
this.iocContainer = new inversify_1.Container();
this.app = express();
this.httpServerInstance = null;
this.authenticationMiddleware = null;
this.errorHandler = (transaction) => transaction.send.internalServerError();
this.notFoundHandler = (transaction) => transaction.send.notFound();
this.expressConfigurationHandler = null;
// Hooks
this.onTransactionStart = new simple_event_1.SimpleEvent();
this.onTransactionEnd = new simple_event_1.SimpleEvent();
this.onListen = new simple_event_1.SimpleEvent();
this.onRegisterEndpoint = new simple_event_1.SimpleEvent();
}
start(port) {
// Run express configuration handler if any. This has to be done here so
// iocContainer is configured and ready for use
if (typeof this.expressConfigurationHandler === 'function') {
this.expressConfigurationHandler(this.app, this.iocContainer);
}
// Register middleware to add transaction into request object
this.app.use(this.createAddTransactionMiddleware());
// Register own endpoints
this.registerEndpoints();
// Trigger a onListen hook when server starts listening the given port
return new Promise(resolve => {
this.httpServerInstance = this.app.listen(port, () => {
this.onListen.fire(port);
resolve();
});
});
}
stop() {
return new Promise(resolve => {
if (!this.httpServerInstance) {
return resolve();
}
this.httpServerInstance.close(() => resolve());
});
}
setAuthenticationMiddleware(middleware) {
this.authenticationMiddleware = middleware;
return this;
}
setErrorHandler(handler) {
this.errorHandler = handler;
return this;
}
setNotFoundHandler(handler) {
this.notFoundHandler = handler;
return this;
}
configureIocContainer(handler) {
handler(this.iocContainer);
return this;
}
configureExpressInstance(handler) {
this.expressConfigurationHandler = handler;
return this;
}
getExpressInstance() {
return this.app;
}
/**
* Hydrates the Express application with endpoints and authentication middleware
*/
registerEndpoints() {
const prefixUrl = (url) => (url.charAt(0) === '/') ? url : `/${url}`;
// Convert auth middleware into express middlewares
const authExpressMiddleware = (this.authenticationMiddleware) ?
transaction_utils_1.createExpressMiddleware(this.authenticationMiddleware, this.iocContainer)
: null;
// Get endpoints from the Reflect metadata
const endpoints = Reflect.getMetadata(metadata.ENDPOINTS_METADATA_SYMBOL, Reflect) || [];
endpoints.forEach(endpoint => {
// Extract needed information from the endpoint object
const { method, public: isPublic, target, middleware } = endpoint;
const url = prefixUrl(endpoint.url);
// Convert endpoint middlewares into express middlewares
const expressMiddleware = middleware.map(middleware => transaction_utils_1.createExpressMiddleware(middleware, this.iocContainer));
// Add authentication middleware into beginning of the middleware array
// if endpoint needs a auhenticaiton
if (!isPublic && authExpressMiddleware) {
expressMiddleware.unshift(authExpressMiddleware);
}
// Trigger a onRegisterEndpoint hook
this.onRegisterEndpoint.fire({
url,
method,
isPublic
});
// Register endpoint to Express app
this.app[method](url, expressMiddleware, (req, res, next) => {
// Set target into iocContainer and retrieve it immediatly,
// by doing this we have now instance with dependencies injected
// by inversify
this.iocContainer.bind(target).toSelf();
const instance = this.iocContainer.get(target);
const handler = instance.handle.bind(instance);
this.iocContainer.unbind(target);
transaction_utils_1.createExpressHandler(handler)(req, res, next);
});
});
// Register not found and error handlers
this.notFoundHandler && this.app.use(transaction_utils_1.createExpressHandler(this.notFoundHandler));
this.errorHandler && this.app.use(transaction_utils_1.createExpressErrorHandler(this.errorHandler, this.iocContainer));
}
/**
* Creates a middleware for constracting the Transaction and injecting it into express request
*/
createAddTransactionMiddleware() {
return (request, response, next) => {
const correlationId = uuid.v4();
const transaction = new transaction_1.default(request, response, correlationId, () => this.onTransactionEnd.fire(transaction));
// Trigger a onTransactionStart -hook
this.onTransactionStart.fire(transaction);
// Set transaction into request
request.transaction = transaction;
next();
};
}
}
exports.HorsServer = HorsServer;