Overview

Midnight is an open source web framework for node.js without external dependencies.

Features:

Here's a simple Midnight application:

const midnight = require("midnight");
const app = midnight();

app.route("/", (req, res) => {
	res.send("Hello world!");
});

app.start();

That's it.

Guide

Getting started

Let's take a closer look on how to get application up and running.

Install Midnight using Yarn:

yarn add midnight

Or using Node Packaged Modules:

npm install midnight

Prerequisites for running a Midnight application are now installed. Next step is to create a new application.

New application

Create new file called app.js with following contents:

const midnight = require("midnight");
const app = midnight();

app.route("/", (req, res) => {
	res.send("Hello world!");
});

app.start();

Next step is to run the application:

node app.js

Well done, application is now running at http://localhost:8080.

Scroll down to learn more about the Midnight framework.

Router

Setting a new route is super easy:

app
  .route("/", (req, res) => {
    res
      .status(200)
      .content("text/plain")
      .send("Hello world!");
  })
  .method(["GET", "POST"]); // Allow GET and POST requests  

Route parameters can be captured as shown below:

app
  .route("/post/:id", (req, res) => {
    app.log.info("Get post id %s", req.params.id);
    res
      .status(200)
      .set("Content-Type", "text/plain")
      .send("Hello world!");
  })
  .get(); // Allow GET requests only

Define route groups:

const group = app.route("/group/:group");

// Apply middleware to route group
group.use((req, res, next) => {
  res.set("Has-Parent-Middleware", true);
  next();
});

// /group/:group
group.route("/", (req, res) => {
  res.send("Route group /");
});

// /group/:group/foo/:id
group.route("/foo/:id", (req, res) => {
  res.send(req.params);
});

See Route and app.route for more detailed documentation.

Middleware

Midnight is fully compatible with Connect framework.

Following example will populate request.query object containing HTTP GET parameters:

const compression = require("compression");

// Gzip/deflate all outgoing responses
app.use(compression());

app.route("/", (req, res) => {
  res.status(200).send(JSON.stringify(req.query));
});

In case a route specific middleware is required:

const bodyParser = require("body-parser");

app
  .route("/", (req, res) => {
    res.status(200).send(JSON.stringify(req.body));
  })
  // Parse urlencoded request bodies into req.body
  .use(bodyParser.urlencoded({ extended: false }));

It's also possible to create a custom middleware:

app.use((req, res, next) => {
  // Use X-Custom-Header response header with every request
  res.set("X-Custom-Header", true);
  next();
});

app.route("/", (req, res) => {
  res.send("Response status is 200");
});

Static files

Serving static files with Midnight and Connect is easy:

const serveStatic = require("serve-static");

app.use(serveStatic(path.join(app.config.root, 'static')))      

Example above serves static files with a following directory layout:

Requesting http://localhost:8080/stylesheets/style.css serves the stylesheets with ease.

Plugins

Midnight has a flexible plugin system built-in to easily extend the application.

Here's how to create a new plugin:

const skyColorPlugin = {
  name: "sky-plugin",
  attach: (app, options = {}) => {
    // Called when plugin is attached
    // Options can be passed as a parameter
    this.options = options;
  },
  init: (app, next) => {
    // Called when the application is initialized
    app.sky = this.options.color;

    // Call next() when done extending the app
    next();
  }
};

// Attach the plugin with parameters
// Plugin extends the app with `sky` property
app.plugin(skyColorPlugin, {
  color: "blue"
});

Application server won't start until all the plugins are initialized.

Examples

Plenty of more examples included with the source code.

Go to Github and browse the repository.

API Reference

Application

app.configure(config)

app.configure({
  port: 9000,
  views: '/templates'
});

app.log.info('Server will run at port %s', app.config.port);

To reveal the default configuration:

app.log.info(JSON.stringify(app.config));

{
  'host': '127.0.0.1',
  'port': 8080,
  'views': '/views',
  'root': '/application/root/path',
  'env': 'development',
  'version': '0.0.1'
}

app.start([config])

Start the application.

app.start();

Alternatively, start the server with configuration parameter.

app.start({
  port: 9000
});

app.log

Set log level:

// Default logging level
app.config.log = app.log.level.info;

Logging.

// Trace
app.log.trace('Finely detailed information');

// Debug
app.log.debug('Detailed information');

// Info
app.log.info('Interesting runtime events');

// Warning
app.log.warn('Runtime situations that are undesirable');

// Error
app.log.error('Runtime errors or unexpected conditions');

// Fatal
app.log.fatal('This is very bad');

app.route(pattern, [function])

Declare a new route:

app.route('/post/:id', (req, res) => {
  res.send(req.params);
});

Use regular expression as a route pattern:

// Accept requests with url /route/{parameter}
app.route(new RegExp('^\\/route\/(?:([^\\/]+?))\\/?$', 'i'), (req, res) => {
  res.send(req.params);
});

Routes can be used like below to set additional parameters:

app.route('/', (req, res) => {
  res.send('Hello world!');
}).get(); // Allow GET requests only

Leave handler argument out to simply use route specific middleware:

// Use custom middleware for specific route only
app.route('/specific/route').use(yourMiddlewareHere);

See Router for more examples.

app.utils

Provides few useful tools, see Github for complete list.

app.use(handler)

Use middleware for all the requests.

app.use(globalMiddlewareHere);

app.plugin(plugin, [options])

Attach a plugin to the application.

app.plugin(plugin);

See Plugins for more examples.

Route

See Router for examples on how to define routes.

route.method(method)

Specify allowed methods for the route

// Accept GET requests
route.method('GET');

// Accept GET, POST and DELETE requests
route.method(['GET', 'POST', 'DELETE']);

Above is equivalent to:

// Accept GET requests
route.get();

// Accept GET, POST and DELETE requests
route.get().post().delete();

All HTTP methods are allowed in case no method is specified.

route.get()

Allow GET requests.

route.get();

route.post()

Allow POST requests.

route.post();

route.put()

Allow PUT requests.

route.put();

route.delete()

Allow DELETE requests.

route.delete();

route.use(handler)

Use middleware for specific route.

const bodyParser = require("body-parser");

app
  .route("/", (req, res) => {
    res.status(200).send(JSON.stringify(req.body));
  })
  // Parse urlencoded request bodies into req.body
  .use(bodyParser.urlencoded({ extended: false }));

route.route

Define subroute (route group)

const group = app.route("/group/:group");

// /group/:group/foo/:id
group.route("/foo/:id", (req, res) => {
  res.send(req.params);
});
    

Request

request.method

Get request method.

request.get(header)

Get request header.

request.get('Content-Type');

request.content()

Get request Content-Type.

request.content();

Response

response.status(code)

Set status code.

response.status(200);

response.set(key, [value])

Set response header.

// Set response header
response.set('Access-Control-Allow-Origin', '*');

// Remove response header
response.set('Access-Control-Allow-Origin');

response.get(header)

Get response header.

response.get('Content-Type');

response.content([type])

Set Content-Type response header.

// Set response header
response.content('image/png');

// Get response header
response.content();

response.redirect(location)

Redirect to a location.

response.redirect('/');

response.encoding(encoding)

Set response encoding.

response.encoding('utf-8');

response.send(response)

Send a response.

// Content-Type: text/html
response.send('Hello world!');

Depending on the object type different Content-Type header is used:

// Content-Type: application/json
response.send({ name: 'Harry' });

// Content-Type: application/json
response.send(['Apples', 'Oranges']);

// Content-Type: application/octet-stream
response.send(new Buffer('Hello world!'));

This method automatically adds Content-Length header for the response.