Skip to content

Instantly share code, notes, and snippets.

@digitalsadhu
Last active December 21, 2018 07:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save digitalsadhu/4a3839983a8a384882be28154f830c62 to your computer and use it in GitHub Desktop.
Save digitalsadhu/4a3839983a8a384882be28154f830c62 to your computer and use it in GitHub Desktop.
App scaffold proposal
'use strict';
class AppScaffold {
/*
Method used to register a plugin function that will be run in the constructor
Plugins can be used to
* register middleware
* add functions to the startup queue
* add functions to the shutdown queue
* register metrics streams to be consumed
* log messages
* access the express app instance (register routes etc)
* access the app scaffold instance including user options
*/
static plugin(fn) {
if (!this.plugins) this.plugins = [];
this.plugins.push(fn);
}
constructor(options = {}) {
this.startQueue = [];
this.stopQueue = [];
this.metricsStreams = [];
this.middlewares = [];
this.app = express();
this.options = {
port: 8080,
grace: 0,
logger: null,
metrics: false,
prometheus: null,
...options,
};
this.log = abslog(this.options.logger);
// run all registered plugins
for (const plugin of this.constructor.plugins) {
plugin(this);
}
// mount all registered middleware
for (const middleware of this.middlewares) {
this.app.use(middleware);
}
// setup app start
this.appStart = new AppStart(this.app, {
port: this.options.port,
grace: this.options.grace,
logger: this.log,
});
this.registerMetrics(this.appStart.metrics);
// setup metrics consumption, compose all streams registered with this.registerMetrics
if (this.options.metrics) {
Object.defineProperty(this, 'consumer', {
value: new MetricsConsumer({ client: this.options.prometheus }),
});
pipeline(...this.metricsStreams, this.consumer, err => {
if (err) this.log.error('an error occurred', err);
});
}
}
/*
Register a metrics stream created by @metrics/client to be consumed
*/
registerMetrics(stream) {
this.metricsStreams.push(stream);
}
/*
Register a middleware function to be mounted into the express app
*/
registerMiddleware(fn) {
this.middlewares.push(fn);
}
/*
Register a function (that returns a promise) that will be run immediately before the app starts
*/
onStart(fn) {
this.startQueue.push(fn);
}
/*
Register a function (that returns a promise) that will be run immediately before the app stops
*/
onStop(fn) {
this.stopQueue.push(fn);
}
/*
Public API to start the app
*/
async start() {
// run through on start queue
for (const op of this.startQueue) {
await op();
}
for (const op of this.stopQueue) {
this.appStart.proc.set(async next => {
await op();
next();
});
}
await this.appStart.start();
}
/*
Public API to stop the app
*/
stop() {
await this.appStart.stop();
}
}
module.exports = AppScaffold;
'use strict';
const ExampleApp = require('./custom-app.js');
const example = new ExampleApp({
port: 3004,
grace: 5000,
});
example.app.get('/', (req, res) => {
res.send('this is my pre-configured app');
});
example.start();
'use strict';
const ExpressTiming = require('express-timing-middleware');
const AppScaffold = require('./app-scaffold');
class ExampleApp extends AppScaffold {}
// add express timing middleware plugin
ExampleApp.plugin(instance => {
const timing = new ExpressTiming();
instance.registerMiddleware(timing.middleware());
instance.registerMetrics(timing.metrics);
});
module.exports = ExampleApp;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment