Lightweight Node/Express framework based on Coldbox
Nodebox is a lightweight framework for Node that acts as an Express middleware to quickly set up routes based on file system paths. Nodebox handles all of your routing, and follows simple rules to translate urls into object and function calls so you can worry about creating your project instead of how to wire everything up.
Pull Nodebox into your project and initialize it:
npm install nodebox-framework
npx nodebox init
This will set up your package.json and the basic directory structure needed, which looks like:
/
- handlers/
- views/
- layouts/
- public/ (this is where you can put assets like css, images, or external javascript files)
- css
To see the test site, run:
npm start
After installing the package, add it as middleware:
const express = require('express');
const app = express();
const Nodebox = require('nodebox-framework').Nodebox;
const nodeBox = new Nodebox({loglevel:'debug'});
app.use(nodeBox.getMiddleware(app));
// Listen to port 3000
app.listen(3000, function () {
console.log('Dev app listening on port 3000!');
});
The different loglevels available are:
- TRACE
- DEBUG
- INFO (default)
- WARN
- ERROR
Nodebox translates your urls into system calls. For example, if you went to the url:
<site>/home/about
Nodebox translates that call to load a handler named home
(that returns a class), and call the about
method. (which renders things). The handlers are stored in the /handlers
folder of your project.
For the above example, the directory structure would look like:
/
- handlers/
- home.js
- views/
- layouts/
Nodebox also supports more complex urls by nesting the directory structure. A url like /home/foo/bar
would be supported by a directory structure like:
/
- handlers/
- home/
-foo.js
- home.js
- views/
- layouts
Views and layouts are composed using the template functionality of lodash. As you can probably guess, these are located in the views
and layouts
folders respectively. A handler doesn't necessarily need a layout, but it does need a view to know HOW to render.
There are a couple of different ways to assign views, layouts, and variables but the easiest is to initialize the Nodebox router with a config object that contains everything it needs.
Below is an example handler:
const NodeboxHandler = require('nodebox-framework').NodeboxHandler;
class Main extends NodeboxHandler {
preEvent() {
console.log('preEvent happens here!');
}
postEvent() {
console.log('postEvent happens here!');
}
home() {
this.log.info('In Home');
this.nbr.set({
view: 'home.html',
layout: 'layout.main.html',
useLayout: true,
vars: {
subtitle: 'varSubtitle',
view:{
viewvar: 'varviewvar!'
}
}
})
this.nbr.render();
}
json() {
this.nbr.set({
layout: 'json',
vars: {
foo:'foo',
bar:'bar'
}
})
this.nbr.render();
}
}
module.exports = Main;
The first thing to note is that your handler must extend the NodeboxHandler
class. This gives you a built in constructor that take the express req
and res
objects, as well as a loglevel
. The built in constructor also gives you an instance of the Nodebox Renderer at this.nbr
and the logger at this.log
. If you want to override the default log level, you can do so by adding a constructor to your class that looks like:
class MyClass extends NodeboxHandler {
constructor(req, res) {
super(req, res, 'debug'); //add your loglevel as the third argument
}
}
Finally, set up your handler method (in this case, I'm writing one for the default event: main/home
:
home() {
this.nbr.set({
view: 'home.html',
layout: 'layout.main.html',
useLayout: true,
vars: {
subtitle: 'varSubtitle',
view:{
viewvar: 'varviewvar!'
}
}
})
this.nbr.render();
}
Let's break down the config object above.
- view: the view you want to render (located relative to
/views
) - layout: the layout you want to render (located relative to
/layouts
) - NOTE: There is a special built-injson
layout you can use to render json data - useLayout: directs the renderer to use a layout or to just render the view
- vars: template variables for replacement (lodash template). All the variables in the
view
object are passed directly into the view template.
Finally, call render on the Nodebox renderer to send a response back through express.
<head>
<link rel='stylesheet' href='/css/main.css'/>
</head>
<div id="container">
<h1> Layout!</h1>
<h3><%= subtitle %></h3>
<div id="view">
<%= view %>
</div>
</div>
In the example above, you can see some of lodash's template engine at work. You can reference any variable passed in the var structure here. Also of note, the special view
variable is the location in the page that the view is rendered.
<h2>text from home.html</h2>
<p>view var: <%= viewvar %></p>
Much in the same way the layout works, the view also uses the lowdash templating. In addition, you can see the viewvar
variable from the original structure being used here.
Here is an example of a handler method that returns json. Note, whatever is in the vars object gets sent to the browser:
json() {
this.nbr.set({
layout: 'json',
vars: {
foo:'foo',
bar:'bar'
}
})
this.nbr.render();
}
The directory structure for the examples above would ultimately end up looking like this:
/
- handlers/
- home.js
- views/
- home.html
- layouts/
- layout.main.html
- public/
- css/
-main.css
As you might have noticed, there are preEvent
and postEvent
functions in the handler. This is a special interface that Nodebox looks for in every handler that lets you run arbitrary code before and after a handler function is executed. You can use this for things like checking to see if a user is authenticated before executing anything in that handler.
To stop processing, return false
from the preEvent
in your handler, and the route's main event and postEvent won't fire.