larvitbase

4.0.72 • Public • Published

Build Status

Micro web framework

Index

What is it?

Top

A scaled down version of Express. It is as micro as micro can be, it only runs an array of middlewere functions, nothing more.

Why?

Top

  • More flexibility due to all functionality being in the middleware functions (no built-in "helpers", router, view system etc)
  • In contrast to Express, it is possible to run a middleware before the router, or after the controller
  • Better separations of concerns (routing, view system etc should not be a part of the base framework!)
  • No dependencies in production (only development dependencies)
  • Less is more

Installation

Top

npm i larvitbase

Usage

Top

In case you're building a full webb system, you probably want to go directly to larvitbase-www or if you're building an API, you might want to look at larvitbase-api. Both of these is larvitbase plus some middlewares to handle the basics of each scenario.

Minimal basic example

Top

This will create a http server on port 8001 that will print "Hello world" whatever you send in.

const	App	= require('larvitbase');

let app = new App({
	'httpOptions': 8001, // Listening port
	'middlewares': [
		function (req, res) {
			res.end('Hello world');
		}
	]
});

app.start(function (err) {
	if (err) throw err;
});

Routing

Top

Routing is how to match a special URL to a specific piece of code or pieces of code.

There is no built in routing, but it is easy to make your own or use the more fully fledged larvitrouter.

Roll your own

Top

Something like this:

const	App	= require('larvitbase');

let	app;

function router(req, res, cb) {
	if (req.url === '/') {
		req.controller	= controllerOne;
	} else if (req.url === '/foo') {
		req.controller	= controllerTwo;
	} else {
		req.controller	= notFound;
	}
	cb();
}

function runController(req, res, cb) {
	req.controller(req, res, cb);
}

function controllerOne(req, res, cb) {
	res.end('This is controllerOne! Hepp!');
}

function controllerTwo(req, res, cb) {
	res.end('This is the second controller function! Hola!');
}

function notFound(req, res, cb) {
	res.statusCode	= 404;
	res.end('The URL matched no controller. Ledsen i ögat. :(');
}

app = new App({
	'httpOptions': 8001, // Listening port
	'middlewares': [
		router,	// First run the router
		runController	// Then run the routed controller
	]
});

app.start(function (err) {
	if (err) throw err;
});
Test your application

From the path of your application, type:

node ./index.js

Then go to a browser and go to http://localhost:8001 and you should see "This is controllerOne! Hepp!". Test the URL:s /foo and /something as well and see what happends.

larvitrouter

Top

For a bit larger application, it is often desireble with a more competent router. For this we have larvitrouter. It will resolve paths to controller files as well as template files and static files. See the documentation for an in depth guide. Here follows a small example:

First install it:

npm i larvitrouter
index.js
const Router = require('larvitrouter'),
      router = new Router(),
      App    = require('larvitbase');

let	app;

function runRouter(req, res, cb) {
	router.resolve(req.url, function (err, result) {
		req.routeResult = result; // Store the route result on the request so we can access it from elsewhere
		cb(err);
	});
}

function runController(req, res, cb) {

	// A controller file was found!
	if (req.routeResult.controllerFullPath) {
		const controller = require(req.routeResult.controllerFullPath);
		controller(req, res, cb);

	// No controller file was found
	} else {
		res.statusCode	= 404;
		return res.end('Not found');
	}
}

app = new App({
	'httpOptions': 8001, // Listening port
	'middlewares': [
		runRouter,
		runController
	]
});

app.start(function (err) {
	if (err) throw err;
});
controllers/foo.js

To make the URL /url work on our application, we create this file and save it like this:

'use strict';

exports = module.exports = function controllerFoo(req, res, cb) {
	res.end('Foo custom page');
	cb();
}
controllers/default.js

The default controller is a bit special. It will match both / and /default.

'use strict';

exports = module.exports = function controllerFoo(req, res, cb) {
	res.end('Default page');
	cb();
}

Templates

Top

In this section we're going to implement EJS template engine.

First install dependencies:

npm i larvitbase ejs

Top

index.js
const ejs	= require('ejs'),
      App = require('larvitbase');

let	app;

function controller(req, res, cb) {
	// Set some different data depending on URL
	res.data	= {};

	if (req.url === '/') {
		res.data.someText	= 'First page';
	} else if (req.url === '/foo') {
		res.data.someText	= 'A sub page';
	} else {
		res.data.someText	= 'No page';
	}

	cb();
}

function renderTemplate(req, res, cb) {
	res.body	= ejs.render('<html><body><h1><%= someText %></h1></body></html>', res.data);
	res.end(res.body);
	cb();
}

app = new App({
	'httpOptions': 8001, // Listening port
	'middlewares': [
		controller,
		renderTemplate
	]
});

app.start(function (err) {
	if (err) throw err;
});

Forms

Top

To parse forms (and file uploads, url parameters and more) we can use larvitreqparser.

Install dependencies:

npm i larvitbase larvitreqparser

index.js

const ReqParser	= require('larvitreqparser'),
      reqParser = new ReqParser(),
      App       = require('larvitbase');

let	app;

function controller(req, res, cb) {
	res.body = '<html><body><form method="post">';
	res.body += '<p>Write something: <input name="foo" /></p>';
	if (req.formFields && req.formFields.foo) {
		res.body += '<p>Something was written: "' + req.formFields.foo + '"</p>'
	}
	res.body += '<p><button type="submit">Click me</button></p>';
	res.body += '</body></html>';

	res.end(res.body);
	cb();
}

app = new App({
	'httpOptions': 8001, // Listening port
	'middlewares': [
		reqParser.parse.bind(reqParser),
		controller
	]
});

app.start(function (err) {
	if (err) throw err;
});

Static Files

Top

To feed static files, we are going to use a router to determine if the URL points to a static file, and then send to stream the file to the client.

Install dependencies:

npm i larvitbase larvitrouter send

index.js

const Router = require('larvitrouter'),
      router = new Router({'staticsPath': __dirname + '/public'}), // This is the default, but we're explicit here
      send   = require('send'),
      App    = require('larvitbase');

let	app;

function runRouter(req, res, cb) {
	router.resolve(req.url, function (err, result) {
		req.routeResult	= result;
		cb(err);
	});
}

function controller(req, res, cb) {

	// Static file found! Stream it to the client.
	if (req.routeResult.staticFullPath) {
		send(req, req.routeResult.staticFullPath).pipe(res);

		// End the controller here, nothing more should be sent to the client
		return cb();
	}

	// What to show if no static file is found:
	res.body = '<html><body><h1>Show a static file:</h1>';
	res.body += '<p><a href="foo.txt">foo.txt</a></p>';
	res.body += '</body></html>';

	res.end(res.body);
	cb();
}

app = new App({
	'httpOptions': 8001, // Listening port
	'middlewares': [
		runRouter,
		controller
	]
});

app.start(function (err) {
	if (err) throw err;
});

public/foo.txt

Just save a text file with whatever content in hour applications path + public/foo.txt

Error handling

Top

In case something goes wrong inside any of your middlewares and an error is returned, we need to handle that somehow. This is how:

const App = require('larvitbase');

let app;

app = new App({
	'httpOptions': 8001, // Listening port
	'middleware': [
		function (req, res, cb) {
			return cb(new Error('Something went wrong! :('))
		}
	]
});

// Handle errors in one of the middleweres during a request
app.on('error', function (err, req, res) {
	res.statusCode	= 500;
	res.end('Internal server error: ' + err.message);
});

app.start(function (err) {
	if (err) throw err;
});

Logging

Top

The App takes a log option and it requires it to be an object with the following methods: silly(), debug(), verbose(), info(), warn() and error().

Historically we've used winston, but any object with the above methods will work fine. If no log instance is supplied, an instance of larvitutils Log will be used with level "info".

Log levels used:

  • error - Fatal! Application should not continue to run at all
  • warn - Important problem. Application might be able to continue, but this should be addressed to maintain stability.
  • info - Important information. Probably not a problem, but information of high value to sysops.
  • verbose - Nice-to-have information. Statistis about each request run time and such.

Do not use in production

  • debug - Debug information. Further statistics and other debug info. Will flood your logs if used in production!
  • silly - Silly amounts of information. Will flood your logs even if not in production, your terminal will explode.

Middleware functions

Top

Middleware functions are compatible with Express, and follow the same principles:

Middleware functions can perform the following tasks:

  • Execute any code.
  • Make changes to the request and the response objects.
  • End the request-response cycle.
  • Call the next middleware function in the stack.

Larvit middlewares

Top

Versions

Current Tags

Version History

Package Sidebar

Install

npm i larvitbase

Weekly Downloads

100

Version

4.0.72

License

ISC

Unpacked Size

29.1 kB

Total Files

11

Last publish

Collaborators

  • vktr
  • lilleman
  • gagge
  • klabbekluck
  • jaggu-snorr
  • jaggu-larvit-npm-publish