server-lite

2.0.0 • Public • Published

Server Lite

This is a lightweight webserver that is meant to take all the complexities out of creating and running a webserver, without losing any of the configuration options. This has been built to be weakly opinionated, meaning that all of the decisions that are made typically can be modified. The included utilities and handlers mean that the focus of a user of server-lite is on code and functionality, without worry about the backend services or processes.

The Fun Stuff

Build Status

Code Climate

Issue Count

Test Coverage

Dependency Status

npm Version

Why Another Web Server?

Most webservers that exist fall into 2 categories: Not ready for production or Strongly opinionated. This implementation is meant to solve both of these concerns, creating a production-ready webserver that is weakly opinionated and highly configurable.

Not ready for production

Surprisingly, several webserver projects seem to be super heavy, abandoned, or very limited implementations. It's time for a webserver that is used, maintained and offers a real production solution.

Strongly opinionated

There is nothing worse for developers than working with projects that won't let them tweak, tune or customize functionality. These strong opinions make main-path programming simple, but fail when businesses move off main-path, as they inevitably do.

Getting Started

  • Install the npm in your project: npm install --save server-lite
  • Require the library where needed: const svr = require('server-lite');
  • Enjoy a robust server.

Using server-lite

Utilizing server-lite should be simple and straightforward. There are currently 5 different components that all work together to make webserver functionality rich and flexible. Those are:

  • config => designed to put all of the configuration in one object
  • content => the lightweight, super-powered content manager. This is aware of more than 680 different MIMETypes (and their file extensions), so you don't have to be.
  • handler => pre-built request handling functionality to serve files and automatically concatenate your JavaScript or CSS files, without having to use an external lib.
  • server => a one-call create (based on the config) and a one call start, the rest just works.
  • utils => hooks to manage building response objects, loading files from the filesystem, and managing different types of web content.

Each of these are documented in further detail below, but a simple webserver can be setup as easily as:

const sl = require('server-lite');
const out = new (require('output-manager').Out()); // setup your output manager
function onReq(request, response) { out.i('recieved request'); } // create onRequest method
const cfg = new sl.config({ out: out, port: 8888, onRequest: onReq }); // assign config
const svr = new sl.server.http(cfg); // apply config
svr.start();

config

The config object is all about configuration and single set of information. The nice part about this is that it can be configured for http or https. Each of the properties is set in the constructor, or can be accessed directly on the property at a later time. The param list is:

  • out => {output-manager}
  • httpsOptions => {Hash} of options as defined here or here
  • port => {number} port to start server listening on
  • host => {string} optional host
  • backlog => {number} optional backlog do not set this if you don't understand it
  • path => {string} specifies a unix socket do not set this if you don't understand it
  • exclusive => {boolean} deals with underlying file handle sharing do not set this if you don't understand it
  • onCheckContinue => {Function} deals with status code 100 do not set this if you don't understand it
  • onCheckExpectation => {Function} deals with checks not status code 100 do not set this if you don't understand it
  • onClientError => {Function} called when client has an error
  • onClose => {Function} called when server closes
  • onConnect => {Function} called on HTTP CONNECT
  • onConnection => {Function} called when a stream is established
  • onError => {Function} called on server error
  • onListening => {Function} called when server is listening on port
  • onRequest => {Function} primary request handler
  • onUpgrade => {Function} called on HTTP upgrade requests do not set this if you don't understand it

For more information, it would be good to read the Net#server.listen documentation, as well as the HTTP and HTTPS documentation on nodejs.org

Declaring a new config:

const sl = require('server-lite');
const out = new (require('output-manager').Out()); 
function onReq(request, response) { out.i('recieved request'); }
const cfg = new sl.config({
    out: out,
    port: 8888,
    onRequest: onReq
});

content

The content object is all about making sure MIMETypes and other header information is auto-magically calculated as needed. content doesn't use a class, and instead exports the following methods:

  • binary => meant to return a binary object type with MIMEType of application/octet-stream
  • text => simple text handler to build a text/plain content object. Used a lot for errors and other messages back to the client from the server.
  • byExtension => simple method that looks up the MIMEType from the file extension, and uses the data to build a content object.
  • custom => allows for a custom MIMEType to be assigned
  • mimeTypes => a Hash of all known MIMETypes, and the associated extensions.

Likely most people will have minimal interaction with the content object.

Declaring content:

const sl = require('server-lite');
const out = new (require('output-manager').Out());
let simple = sl.content.text('Simple text');
out.i(JSON.stringify(simple)); // { type: 'text/plain; charset=utf-8', value: 'Simple text', length: 11 }

handler

The handler object is meant to be the more opinionated solutions that you can optionally delegate to. It relies heavily on the utils object, taking it as the first parameter of the constructor. handler has 4 properties and 4 main methods, with over a dozen internals. The properties are:

  • webRoot => root of the webserver on the local filesystem. You can see examples of this in the tests or the examples
  • indexPath => path to the index file for requests to /
  • concatenateJavscriptFolderPath => known folder where all .js files are eligible for concatenation
  • concatenateCssFolderPath => known folder where all .css files are eligible for concatenation

In addition to these properties, you will find 4 main methods:

  • simpleFileBasedWebServer(request, response) => Used to simply serve a file based on the request. The request path is parsed to determin the filePath relative to the webRoot.
  • concatenatedJavscript(request, response) => Used as sugar for concatenateAll => presumes the request is for concatenated .js files in given concatenateJavscriptFolderPath
  • concatenatedCss(request, response) => Used as sugar for concatenateAll => presumes the request is for concatenated .css files in given concatenateCssFolderPath
  • concatenateAll(request, response, folder, ext) => used to look at the request, and concatenate all matching files in webRoot + folder + query name + ext (or extension)

Create a simple file-based webserver:

const sl = require('server-lite');
const out = new (require('output-manager').Out()); // setup your output manager
const handler = new sl.handler();
function onReq(request, response) { handler.simpleFileBasedWebServer('recieved request'); } // create onRequest method
const cfg = new sl.config({ out: out, port: 8888, onRequest: onReq }); // assign config
const svr = new sl.server.http(cfg); // apply config
svr.start();

server

The server object actually exposes 2 main consumer classes (http and https) and an internal reference to slServer for testing and special situations. http and https do exactly what is expected, creating a server of that type, based on a given config.

An example of this:

const sl = require('server-lite');
const out = new (require('output-manager').Out()); // setup your output manager
function onReq(request, response) { out.i('recieved request'); } // create onRequest method
const cfg = new sl.config({ out: out, port: 8888, onRequest: onReq }); // assign config
const svr = new sl.server.http(cfg); // apply config
svr.start();

utils

The utils object has several helpers that make management easier, but unlike handler are not meant to provide a complete solution. utils takes an {output-manager} (optionally, if not provided it creates one) that can be used to write out. There are several methods on utils as defined:

  • errStr => builds an error message and logs the error (typically exceptions/errors caught)
  • buildFromFilePath => returns a content object based on the filePath extension
  • loadDataFromFile => uses promises to async load the data of the file at the given filePath
  • respondWithFileFromPath => takes a {Http.Response} object and loads the given filePath data, and writes the reponse.
  • writeReponse => given the {Http.Response} object and a content object, writes headers and content.

utils are easy to create, and very handy, especially if usage of handler is being considered. This can all be done via:

const sl = require('server-lite');
const out = new (require('output-manager').Out()); // setup your output manager
const utilz = new sl.utils(out);

markup

The markup object allows for templated .html files. The object takes a folder location and a set of default variables that can be replaced. Each file that should be loaded can be passed and the code will either process the file or process an arbitrary template. This is done with:

  • buildFromTemplate => given a template {String} will replace all values with the provided {Object} values
  • buildFromFileWithPartials => given a file path, will recursively parse each include statement and add the appropriate files.

Template files

Template files are standard .html files, with special markup.

  • <%foo%> => will look for the property foo in the mappedVars or defaultValues arrays. If the value is set in the defaultValues, it can be overridden by the mappedVars
  • <%= tmp/foo/bar %> => uses the provided templateRootFolder as the base path, and then appends these values (and .html) to the base path.
  • Inside of the templates, js can be executed. This allows for small inline configurations. Additionally, variables can be passed into those snippets, using the %{foo} syntax. Some examples of this:

index.html

<%= [1,2,3,4,5].map(v => '<h3>' + v + '</h3>').join(' '); %>
<%= %{letterArray}.map(v => '<h3>' + v + '</h3>').join(' '); %>

renderer.js

const partialMarkup = new slMarkup(utilz, { title: 'My Cool Title', letterArray: "['a', 'b', 'c', 'd', 'e']" }, templateRoot);
output = partialMarkup.buildFromFileWithPartials(htmlRootFolder + '/index.html');

IT IS IMPORTANT TO NOTE the array must be passed as a string literal, as the entire thing is handled as a string literal until it is processed.

Things to remember

  • Everything is assumed to be utf-8 or utf8

Dependencies and Why

This is used to manage all output. It comes with great string formatting from the string-utilz package and date management from the date-utilz. This is a super-lightweight output-manager that helps to minimize disruptions in your code. Because of the flexibility, you can easily direct logs to the console, files, or a stream. Have a look at the example libraries for more information on how to do this.

As much as makes sense, server-lite works to use standard conventions for async processes, and the promise library helps to achieve that.

Slack

This is one of several projects that are in the works, so feel free to reach out on Slack. Please email slack at maddhacker dot com for an invite.

Issues

Please use the Issues tab to report any problems or feature requests.

Change Log

All change history can be found in the CHANGELOG.md file.

Example Repositories

Using server-lite to build a static file webapp (could be SPA)...

Package Sidebar

Install

npm i server-lite

Weekly Downloads

3

Version

2.0.0

License

Apache-2.0

Unpacked Size

181 kB

Total Files

40

Last publish

Collaborators

  • maddhacker