Plug and play, restful, real-time application framework for single page apps.
A regular directory becomes a valid app if it has one or more of the following:
- An environment settings module with the name
environment.js
- A middleware module with the name
middleware.js
- A socket subscription module with the name
sockets.js
- A static content directory with the name
public
- A routes module with the name
routes.js
All of the default file/directory names above can be changed by options in
apper.json
file in any directory.
For usage as command-line tool:
$ npm install -g apper
For usage in code:
$ npm install apper
Just open the shell (or command prompt) in the app directory (with one or more of the above-mentioned files), and run:
$ apper
And open http://localhost:8000
in browser.
Run apper -h
for all the command-line options.
A more specific deployment might look like:
$ apper --port 8080 --address 127.0.0.0 code/src
Here, code/src
is the root path of the application code, relative to current
directory from which apper
is run. It defaults to the current directory itself.
To display internal logs during development (helpful), just set DEBUG
environment
variable to apper:*
$ DEBUG=apper:* apper --port 8000
For a working example, check out test/sample
which is used by the tests.
Just run apper
in the shell there and read the tests to get a better understanding.
[Express] 5 isn't enough. It lacks structure and conventions.
Apper provides:
- Much needed structure to server-side code with strong conventions
- Reliable directory hierarchy for code based on REST end-points
- Design for real-time right off the bat
- Transparent minification & bundling for single page apps
The core idea of apper
is to enable easy REST api based node.js apps, especially
useful for Single Page Applications.
Apper lets you create bigger apps by using smaller independent chunks as subapps. Simply place individual subapps anywhere in the directory hierarchy, and they get exposed under a relative base URL.
Nested subapps are totally cool and highly encouraged. In fact, simply by moving a subapp directory to another directory updates the exposed relative URL of that subapp. No frills.
+ root/
- server.js (See Programmatic Usage section below)
- routes.js (GET /login)
+ public/
- index.html
- main.js
+ api/
- routes.js (GET /, POST /)
+ items/
- middleware.js
- routes.js (GET /, GET /last)
routes.js files above are supposed to specify route handlers for paths mentioned against them.
Read below for code samples for all of them.
GET /
GET /login
GET /api
POST /api
GET /api/items
GET /api/items/last
The route /
serves index.html
in public/
by default.
Can be overridden by including a route GET /
in routes.js
.
The following modules get initialized on the subapp in order:
environment.js
to set environment settings usingapp.set
(like Express)middleware.js
to setup middleware functions usingapp.use
(like Express)sockets.js
to setup WebSocket subscriptions between server and clientpublic
exposed as static content directoryroutes.js
to respond to URL end-points (usingapp.get
,app.post
, etc.)
The root app can contain sub-directories which are complete apps unto themselves. These directories become subapps of the root app (or sub APIs, if you may).
Subapps can be pulled out and placed anywhere in the overall directory structure. This would make them available on the new relative url with respect to the root.
Every subapp directory can be started as a separated app just by running apper
in there.
Due to the directory hierarchy based mounting of subapps, the base URL paths of all subapps are decided by their position in the directory hierarchy.
The subapps can then register for any relative URL route after their base URL and handle requests accordingly.
Create a file (say, server.js
) in your application directory
var app = require('apper')({
port: 8000
});
app.start();
Then, running server.js
will start the application on port 8000. For example:
$ node server.js
To see internal logs (helpful during development), just set the environment variable DEBUG
as follows:
$ DEBUG=apper:* node server.js
Create an application object as usual, and use app.expressApp
as a regular express application
var app = require('apper')();
app.expressApp.listen(5000);
You can use this application as middleware in your regular express app as follows:
var app = require('apper')();
myExpressApp.use(app.expressApp);
Mounting your apper app as an Express subapp is as easy:
var app = require('apper')();
myExpressApp.use('/blog', app.expressApp);
Now the apper application gets confined to /blog
base URL.
Apper synchronously loads its modules when you initialize it. You can start the
server by calling app.start()
.
Constructor options look like this:
var app = require('apper')({
path: '.',
port: 8000,
host: '0.0.0.0',
// Not commonly used. Just use `apper.json` for the configuration
toOpenBrowser: false,
staticDir: 'public',
moduleNames: {
environment: 'environment'
middleware: 'middleware',
routes: 'routes',
sockets: 'sockets'
},
mountPath: ''
});
app.start();
The default values for the options (path/port/etc) are as shown above. The options mean the following:
path
: Path for the directory to be taken as the root application.port
: Port number on which to expose the application.host
: Host name to be used for the application (127.0.0.1
,localhost
,0.0.0.0
, etc).toOpenBrowser
: Whether to open the system default browser with the created application.staticDir
: Name of the static content directory.moduleNames.*
: As discussed below in Structure of Modules.mountPath
: Base URL to mount this application on. Used internally for mounting subapps.
Server automatically starts a single socket.io WebSocket server that works across all subapps but maintains separate namespaces for all communication with different subapps.
Get an Express-based app object and run express methods like
app.set
, app.use
, app.get
, app.post
, etc. on it.
For WebSocket requests, app.sockets
provides the same functionality as
io.sockets
using [socket.io] 6
module.exports = function (app) {
app.set('property', 'value');
// Environment configuration
};
module.exports = function (app) {
app.use(function (req, res, next) {
// middleware code
next();
});
};
module.exports = function (app) {
app.get('/', function (req, res) {
res.send('hey');
});
};
module.exports = function (app) {
app.sockets.on('connection', function (socket) {
socket.on('hey', function (name) {
socket.emit('Hey ' + name + '!');
});
}
};
Client-side code corresponding to sockets.js
looks as simple.
Just include <script src='/socket.io/socket.io.js'></script>
in public/index.html
,
and connect to the socket server like this:
var socket = io();
socket.on('connect', function () {
alert('woohoo!');
});
A subapp client connects by default to its own namespace, as per its directory hierarchy. So you won't have 2 different subapps catching each others socket events.
apper.json
placed in root or any subapp directory controls the following
configuration for the respective app:
-
moduleNames.environment
(Example: 'env')Environment module file name for the current app
-
moduleNames.middleware
(Example: 'mid')Middleware module file name for the current app
-
moduleNames.sockets
(Example: 'sock')Socket subscriptions module for the current app
-
moduleNames.routes
(Example: 'route-definitions')Routes module file name for the current app
-
staticDir
(Example: 'www')Static content directory name for the current app.
-
dirToIgnore
(Example: ['subapp', 'another'])List of directories to not consider as subapps in the current app's directory.
-
bundle
(Example: true/false)Whether to transparently minify and inline all JavaScript and CSS resources, including RequireJS modules. Cached on first use, and served as is thereon.
You can include
require-config.js
in RequireJSbaseUrl
directory to specify custom RequireJS options for bundling. Usually not required.
{
"moduleNames: {
"environment": "env",
"middleware": "mid",
"sockets": "sock",
"routes": "route-definitions"
},
"staticDir": "www",
"dirToIgnore": ["subapp", "another"],
"bundle": true
}
To run tests yourself, install mocha
$ npm install
$ npm install -g mocha
In the project directory, run
$ npm test
Check out the test
directory for usage examples.
MIT