Layman.js
A simple, lightweight manager for HTTP Request <--> Response layers for node.js
Layman.js is a simplified version of Senchalabs's Connect.
It allows you to easily manage multiple layers of Request <--> Response handlers (aka middleware) for your node.js web server.
Contents
- Hello Layman
- Using Routes
- Multiple Layers
- Asynchronous Layers
- Flow Control
- POST & GET
- Nested Layers
- Multiple Hosts
- Organizing Files
- Built-In Web Server
- Layman Configs
- Connect Middleware
- Roadmap
- Change Log
- Feedback
Hello Layman
The following is the 'layman' version for 'hello world'. Layman uses the popular syntax of use(middleware)
, where middleware
is a callback function that will get the request
and response
as it's only arguments:
// Initvar layman = http = site = ; // Add a middleware layer to handle request <--> responsesite; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Note: site
is just a callback function which is used as the main request handler for your server. Learn more about how layman works on the Wiki pages on GitHub (work in progress).
Using Routes
If you want to use a certain middleware only for a specific route, you can do so by specifying the route as the first parameter to the use(...)
function:
// Initvar layman = http = site = ; // This layer handles requests to /some/route/site; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Note: The leading forward-slash /
for the specified route is optional. Using /foo
is the same as using foo
.
Multiple Layers
Multiple request handler layers are added by calling the use(...)
function more than once, each time passing in a new layer that will handle the Request <--> Response pair.
Layers are triggered in order of FIFO - the first layer that was registered is the first layer that will handle the incoming request:
// Initvar layman = http = site = ; // Add the first layersite; // Add the second layersite; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Asynchronous Layers
There are many ways in which a layer can be used asynchronously.
To start with, let's build a most simplistic static-files layer for Layman.
We'll use node's asynchronous fs.readFile(...)
method to serve index.html
:
return true
to indicate an async layer
Example 1: Show casing // Initvar layman = http = files = site = ; // This layer is async because it returns 'true'site; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Note: Because this layer is asynchronous, we res.end(...)
the response within the callback function that is passed into fs.readFile(...)
next()
to use with multiple layers
Example 2: Show casing The example above showed a single layer handling both success and failure of reading a file.
It's common to separate this logic by using multiple layers, where the next
layer would only be used if an async operation failed:
// Initvar layman = http = files = site = ; // This layer is async because it returns 'true'site; // This layer will be used only if the previous layer was unable to read 'index.html'site; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Note: We used next
as the 3rd argument for our async layer. If for any reason we weren't able to read index.html
, the next
layer will be triggered.
Note: Unlike Connect, the next
layer will not receive any data regarding the error that occured. Asynchronous error handling is still work in progress, this behavior might be supported in the future.
autoAsync=true
for run-time based asynchronous control
Example 3: Show casing In certain cases, you may need to determine if the next
layer should be asynchronous or not based on some data that is only available at runtime (when a request is sent to the server \ when a database operation is complete \ when a 3rd party proxy is ready for communication \ ...):
// Initvar layman = http = files = site = ; // Tell Layman to always be in async modesiteconfigsautoAsync = true; // This layer is asynchronous because 'autoAsync' is turned onsite; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Note: No need for the layer to return true
, since autoAsync
is turned on.
Note: Also, we're not using anext
layer here, since the layer handles both success and failure.
Note: Read more about the available configs here
Flow Control
A very common scenario is that one layer needs to end the response without passing control to any of the next layers that were registered. This is done by having that layer return false
.
When a layer (aka callback, aka middleware) returns false
, the response is ended and sent out, and excecution of any following layers stops (internally, layman calls res.end()
):
// Initvar layman = http = site = ; // This layer returns false, so the next layer will never get calledsite; // Why doesn't anyone call me ?site; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Note: Make sure to read about Asynchronous Layers for more info on how to control layers
POST & GET
Layman also supports registering layers that will only handle GET or POST requests (PUT and DELETE are coming soon).
You can also define routes for each of these:
// Initvar layman = http = site = ; // Only triggered for a GET requestsite; // Only triggered for a POST requestsite; // Using routessite; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Nested Layers
In some cases, you're team might need to separate the layers that handle your 'sales' department from those that handle your 'blog'. Using layman, this is a really easy to accomplish:
// Initvar layman = http = site = sales = blog = ; // Setup our blogblog; // Setup the sales departmentsales; sales; // Setup the site to use the correct layman based on the routesite;site; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Multiple Hosts
The example below is similar to the code above - only this time instead of registering sales
and blog
with routes, we register each to it's respective sub-domain:
// Initvar layman = http = site = blog = sales = ; // Setup our blogblog; // Setup the sales departmentsales; sales; // Setup our site to use the correct layman based on the pathsitehost'sales.site.com' sales;sitehost'blog.site.com' blog; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
- use
host
when you need to mount a specifc domain \ subdomain
Organizing Files
In real world applications, you'll want to organize files in a way that is meaningful to your team.
Have a look below at the code below - it's the same 'Multiple Hosts' example from above - just split into 3 different files:
main.js
// FILE: main.js// Initvar http = layman = blog = sales = site = ; // Setup our site's sub-domains to use the dedicated laymansitehost'sales.site.com' sales;sitehost'blog.site.com' blog; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
blog.js
// FILE: blog.js// Initvar layman = blog = ; // Welcome messageblog; // Some articleblog; moduleexports = blog;
sales.js
// FILE: sales.js// Initvar layman = sales = ; // Product pagesales; // After purchasesales; moduleexports = sales;
- Notice that since we setup different hosts for
sales
andblog
inmain.js
- the routes that are used in each file will be relative to the specified sub-domain
Built-In Web Server
Layman also provides a convenience method to start a new web server, making it even easier to implement in your next project:
// Initvar layman = site = ; // Setup our request handler(s)site; // Site is up and running!site;
Note: When using listen()
, the port number is optional, and if omitted, the port number defaults to 80. The above is identical to site.listen()
.
Layman Configs
The following options are currently available for configuration, all of which can also be changed at run-time (using middleware layers) for fine tuning Layman:
// Initvar layman = site = ; // +---------------------------------------------------------------+// | All configs are available directly on layman, under 'configs' |// +---------------------------------------------------------------+ // Should Layman automatically `res.end()` the request after all layers were triggered (default: true)siteconfigsautoEnd = true; // Should Layman consider all layers to be asynchronous by default ? (default: false)siteconfigsautoAsync = false; // When matching the 'hostname' of a request, Layman will use this regex, and compare against the first matching group (default: /(\w+[^:,]*)\:?/)siteconfigshostRegex = /\:?/;
Note: autoEnd
has no effect on asynchronous layers
Connect Middleware
Layman now includes built-in support for Connect middleware.
To use such middleware, pass in a boolean true
as the first argument when registering a layer to use with your site:
// Initvar layman = http = middleware = site = ; // This layer is just a standard Layman layersite; // Using a Connect middleware on '/blog'site; // And finish up with another Layman layersite; // Start a new server, passing in 'site' as the main request <--> response managerhttp;
Note: At the moment, Layman does not support Connect error middleware (might be added in the future)
Note: You can use any of Layman's API such as get(...)
\ post(...)
\ host(...)
and routing (using use(true, '/some/route', middleware)
Note: There are more Connect middleware than we can test against. If you find any bugs, please get in touch @ layman@isnice.me \ open an issue on github
Roadmap
- Create layman middleware and middleware bundles
Play nice with Connect middlewareComplete, Woohoo!- Built-in support for HTTP PUT and HTTP DELETE
Note: Got any ideas on how to make Layman better ?
let us know @ layman@isnice.me
Change Log
v0.1.7
- Fix: Leading slash in route would casue URL parsing error in some URI formats
v0.1.6
- New: Built-in support for Layman ASYNC layers (more info)
- New: Layman is now compatible with Connect middleware (more info)
- Update: Documentation now includes info Layman's
configs
object (more info)
Feedback ?
Layman is still in development, and all feedback is welcome.
Please do get in touch @ layman@isnice.me