apper

by asyncanup

asyncanup / apper

Plug and play, restful, real-time application framework for single page apps

125 Stars 8 Forks Last release: Not found MIT License 169 Commits 34 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

Apper

Plug and play, restful, real-time application framework for single page apps.

Build Status on Travis CI NPM Latest version

Join the chat at https://gitter.im/asyncanup/apper

App

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.

Install

For usage as command-line tool:

$ npm install -g apper

For usage in code:

$ npm install apper

Usage

From command-line

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.

Motivation

Express 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.

Example

Example directory structure

+ 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.

API exposed by the directory structure above

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
.

Core Concepts

Order of initialization of modules

The following modules get initialized on the subapp in order:

  • environment.js
    to set environment settings using
    app.set
    (like Express)
  • middleware.js
    to setup middleware functions using
    app.use
    (like Express)
  • sockets.js
    to setup WebSocket subscriptions between server and client
  • public
    exposed as static content directory
  • routes.js
    to respond to URL end-points (using
    app.get
    ,
    app.post
    , etc.)

Bigger apps composed of small apps

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.

Programmatic Usage

As a module

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

As Express application

Create an application object as usual, and use

app.expressApp
as a regular express application
var app = require('apper')();
app.expressApp.listen(5000);

As Express middleware

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.

More Code

Startup

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.

Structure of modules

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

environment.js

module.exports = function (app) {
    app.set('property', 'value');
    // Environment configuration
};

middleware.js

module.exports = function (app) {

app.use(function (req, res, next) {
    // middleware code
    next();
});

};

routes.js

module.exports = function (app) {

app.get('/', function (req, res) {
    res.send('hey');
});

};

sockets.js

module.exports = function (app) {
    app.sockets.on('connection', function (socket) {

    socket.on('hey', function (name) {
        socket.emit('Hey ' + name + '!');
    });

}

};

Client-side socket code

Client-side code corresponding to

sockets.js
looks as simple.

Just include

 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.

Configuration

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 RequireJS
baseUrl
directory to specify custom RequireJS options for bundling. Usually not required.

Sample
apper.json
configuration file

{
    "moduleNames: {
        "environment": "env",
        "middleware": "mid",
        "sockets": "sock",
        "routes": "route-definitions"
    },
    "staticDir": "www",
    "dirToIgnore": ["subapp", "another"],
    "bundle": true
}

Tests

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.

License

MIT

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.