server-lite
v2.0.0
Published
Lightweight web server for node projects
Downloads
34
Maintainers
Readme
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
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 objectcontent
=> 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 theconfig
) 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 hereport
=> {number} port to start server listening onhost
=> {string} optional hostbacklog
=> {number} optional backlog do not set this if you don't understand itpath
=> {string} specifies a unix socket do not set this if you don't understand itexclusive
=> {boolean} deals with underlying file handle sharing do not set this if you don't understand itonCheckContinue
=> {Function} deals with status code 100 do not set this if you don't understand itonCheckExpectation
=> {Function} deals with checks not status code 100 do not set this if you don't understand itonClientError
=> {Function} called when client has an erroronClose
=> {Function} called when server closesonConnect
=> {Function} called on HTTP CONNECTonConnection
=> {Function} called when a stream is establishedonError
=> {Function} called on server erroronListening
=> {Function} called when server is listening on portonRequest
=> {Function} primary request handleronUpgrade
=> {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 ofapplication/octet-stream
text
=> simple text handler to build atext/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 assignedmimeTypes
=> 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 examplesindexPath
=> path to the index file for requests to/
concatenateJavscriptFolderPath
=> known folder where all.js
files are eligible for concatenationconcatenateCssFolderPath
=> 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 forconcatenateAll
=> presumes the request is for concatenated.js
files in givenconcatenateJavscriptFolderPath
concatenatedCss(request, response)
=> Used as sugar forconcatenateAll
=> presumes the request is for concatenated.css
files in givenconcatenateCssFolderPath
concatenateAll(request, response, folder, ext)
=> used to look at the request, and concatenate all matching files inwebRoot
+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 acontent
object based on thefilePath
extensionloadDataFromFile
=> uses promises to async load the data of the file at the givenfilePath
respondWithFileFromPath
=> takes a {Http.Response} object and loads the givenfilePath
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}
valuesbuildFromFileWithPartials
=> 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 propertyfoo
in themappedVars
ordefaultValues
arrays. If the value is set in thedefaultValues
, it can be overridden by themappedVars
<%= tmp/foo/bar %>
=> uses the providedtemplateRootFolder
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
orutf8
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)...