Documentation

Conceptually server is a function that accepts options and other functions. The heavy lifting is already implemented so you can focus on your project:

// Import the variable into the file
const server = require('server');

// All of the arguments are optional
server(options, fn1, fn2, fn3, ...);

You can also learn Node.js development by following the tutorials.

Getting started

There's a getting started tutorial for beginners. If you know your way around:

npm install server

Then create some demo code in your index.js:

// Import the library
const server = require('server');

// Answers to any request
server(ctx => 'Hello world');

Run it from the terminal:

node .

And open your browser on localhost:3000 to see it in action.

Basic usage

Some of the components are the main function on itself, router and reply. The main function accepts first an optional object for the options, and then as many middleware or arrays of middleware as wanted:

const server = require('server');

server({ port: 3000 }, ctx => 'Hello 世界');

To use the router and reply extract their methods as needed:

const server = require('server');
const { get, post } = server.router;
const { render, json } = server.reply;

server([
  get('/', ctx => render('index.hbs')),
  post('/', ctx => json(ctx.data)),
  get(ctx => status(404))
]);

Then when you are splitting your files into different parts and don't have access to the global server you can import only the corresponding parts:

const { get, post } = require('server/router');
const { render, json } = require('server/reply');

Middleware

A middleware is plain function that will be called on each request. It receives a context object and returns a reply, a basic type or nothing. A couple of examples:

const setname = ctx => { ctx.req.user = 'Francisco'; };
const sendname = ctx => send(ctx.req.user);
server(setname, sendname);

They can be placed as server() arguments, combined into an array or imported/exported from other files:

server(
  ctx => send(ctx.req.user),
  [ ctx => console.log(ctx.data) ],
  require('./comments/router.js')
);

Then in ./comments/router.js:

const { get, post, put, del } = require('server/router');
const { json } = require('server/reply');

module.exports = [
  get('/',    ctx => { /* ... */ }),
  post('/',   ctx => { /* ... */ }),
  put('/:id', ctx => { /* ... */ }),
  del('/:id', ctx => { /* ... */ }),
];

The main difference between synchronous and asynchronous functions is that you use async keyword to then be able to use the keyword await within the function, avoiding callback hell. Some examples of middleware:

// Some simple logging
const mid = () => {
  console.log('Hello 世界');
};

// Asynchronous, find user with Mongoose (MongoDB)
const mid = async ctx => {
  ctx.user = await User.find({ name: 'Francisco' }).exec();
  console.log(ctx.user);
};

// Make sure that there is a user
const mid = ctx => {
  if (!ctx.user) {
    throw new Error('No user detected!');
  }
};

// Send some info to the browser
const mid = ctx => {
  return `Some info for ${ctx.user.name}`;
};

In this way you can await inside of your function. Server.js will also await to your middleware before proceeding to the next one:

server(async ctx => {
  await someAsyncOperation();
  console.log('I am first');
}, ctx => {
  console.log('I am second');
});

If you find an error in an async function you can throw it. It will be catched, a 500 error will be displayed to the user and the error will be logged:

const middle = async ctx => {
  if (!ctx.user) {
    throw new Error('No user :(');
  }
};
Avoid callback-based functions: error propagations is problematic and they have to be converted to promises. Strongly prefer an async/await workflow.

Express middleware

Server.js is using express as the underlying library (we <3 express!). You can import middleware designed for express with modern:

const server = require('server');

// Import the util tool "modern"
const { modern } = server.utils;

// Require the helmet library
const legacy = require('helmet')({ ... });

// Convert it to server.js middleware
const mid = modern(legacy);

// Add it as you'd add a normal middleware
server(mid, ...);

Note: the { ... } represent the options for that middleware since many of express libraries follow the factory pattern.

To simplify it, we can also perform this operation inline:

const server = require('server');
const { modern } = server.utils;

server(
  modern(require('express-mid-1')({ ... })),
  modern(require('express-mid-2')({ ... })),
  // ...
);

Or just keep the whole middleware in a separated file/folder:

// index.js
const server = require('server');
const middleware = require('./middleware');
const routes = require('./routes');

server(middleware, routes);

Abc

// middleware.js
const server = require('server');
const { modern } = server.utils;

module.exports = [
  modern(require('express-mid-1')({ /* ... */ })),
  modern(require('express-mid-2')({ /* ... */ }))
];

Advanced topics

There is a lot of basic to mid-difficulty documentation to do until we even get here. Just a quick note so far:

The main function returns a promise that will be fulfilled when the server is running and can be accessed. It will receive a more primitive context. So this is perfectly valid:

server(ctx => 'Hello world').then(ctx => {
  console.log(`Server launched on https://localhost:${ctx.options.port}/`);
});