Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nodecaf

Package Overview
Dependencies
Maintainers
1
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nodecaf

Nodecaf is a light framework for developing RESTful Apps in a quick and convenient manner.

  • 0.10.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
628
decreased by-4.99%
Maintainers
1
Weekly downloads
 
Created
Source

Nodecaf

Docs for version v0.10.x.

Nodecaf is a light framework for developing RESTful Apps in a quick and convenient manner. Using Nodecaf you'll get:

Get Started

  1. Install the cli utilities: npm i -P -g nodecaf-cli.
  2. Create or just go to your node project directory (you must have a package.json).
  3. Add to your project with: npm i -P nodecaf.
  4. Create a skelleton project with: nodecaf init.
  5. Add your globals in lib/main.js
const Nodecaf = require('nodecaf');
const api = require('./api');

module.exports = () => new Nodecaf({

    // Load your routes and API definitions.
    api,

    // Perform your server initialization logic.
    async startup({ conf, log, global }){

    },

    // Perform your server finalization logic.
    async shutdown({ conf, log, global }){

    }

});
  1. Add your routes in lib/api.js
module.exports = function({ post, get, del, head, patch, put }){

    // Define routes and a list of middleware functions (async or regular no matter).
    get('/foo/:f/bar/:b', Foo.read, Bar.read);
    post('/foo/:f/bar', Foo.read, Bar.write);
    // ...
};
  1. In your app root directory run with: nodecaf run .

Reporting Bugs or Vulnerabilities

If you have found any problems with Nodecaf, please:

  1. Open an issue.
  2. Describe what happened and how.
  3. Also in the issue text, reference the label ~bug or ~security.

We will make sure to take a look when time allows us.

Proposing Features

If you wish to get that awesome feature or have some advice for us, please:

  1. Open an issue.
  2. Describe your ideas.
  3. Also in the issue text, reference the label ~proposal.

Contributing

If you have spotted any enhancements to be made and is willing to get your hands dirty about it, fork us and submit your merge request so we can collaborate effectively.

  • For coding style, we provide an ESLint configuration file in the root of the repository.
  • All commits are submit to SAST and Dependency Scanning as well as Code Quality analisys, so expect to be boarded on your MRs.

Manual

Formerly based on Express, Nodecaf preserves the same interface for defining routes through middleware chains. Check out how to use all the awesome goodies Nodecaf introduces.

Handler Args

In this manual we address as handler args the keys in the object passed as the only argument of any route handler function. The code below shows all handler args exposed by Nodecaf:

function({ req, res, next, query, params, body, flash, conf, log, headers }){
    // Do your stuff.
}

Quick reference:

  • req, res, next: The good old parameters used regularly in middleware-like frameworks.
  • query, parameters, body, headers: Shortcuts to the homonymous properties of req. They contain respectively the query string, the URL parameters, and the request body data.
  • flash: Is an object where you can store arbitrary values. Keys inserted in this object are preserved for the lifetime of a request and can be accessed in all handlers of a route chain.
  • conf: This object contains the entire application configuration data.
  • log: A logger instance. Use it to log events of your application.
  • Also all keys of the globally exposed object are available as handler args for all routes.

Settings File

Nodecaf allow you to read a configuration file and use it's data in all routes and server configuration.

Use this feature to manage:

  • external services data such as database credentials
  • Nodecaf settings such as SSL and logging
  • Your own server application settings for your users

Suported config formats: TOML, YAML, JSON, CSON

Check out how to generate a project with configuration file already plugged in

To load a config file in your app, use the -c flag through the CLI pointing to your conf file path: nodecaf run -c my/conf/path.toml my/app

You can use the config data through it's handler arg in all route handlers as follows:

post('/foo', function({ conf }){
    console.log(conf.key); //=> 'value'
});

Config data can also be passed as an object to the app constructor in lib/main.js:

module.exports = () => new Nodecaf({ conf: { key: 'value' } });

Or a file path if you want to have a fixed config file for setting defaults or any other reason:

module.exports = () => new Nodecaf({ conf: __dirname + '/default.toml' });
Layered Configs

You can also use the app.setup to add a given configuration file or object on top of the current one as follows:

app.setup('/path/to/settings.toml');

app.setup('/path/to/settings.yaml');

app.setup({ key: 'value' });

app.setup({ key: 'new-value', foo: 'bar' });

Layering is useful, for example, to keep a default settings file in your server source code to be overwritten by your user's.

Logging

Nodecaf logs events to stdout by default where each line of the ouput is a JSON object. The log entries will have some default predefined values like pid, hostname etc... In your route handlers, use the functions available in the log object as follows:

function({ log }){
    log.info('hi');
    log.warn({ lang: 'fr' }, 'au revoir');
    log.fatal({ err: new Error() }, 'The error code is %d', 1234);
}

Below is described the signature of the available logging methods.

  • Method Name: one of the available log levels (debug, info, warn, error, fatal)
  • First argument (optional): An object whose keys will be injected in the final entry.
  • Second argument: A message to be the main line of the log. May contain printf-like replacements (%d, %s...)
  • Remaning arguments: Will be inserted into the message (printf-like)

Nodecaf will automatically log some useful server events as described in the table below:

TypeLevelEvent
error after headers sentwarnAn error happened inside a route after the headers were already sent
route errorerrorAn error happened inside a route and was not caught
fatal errorfatalAn error happened that crashed the server process
requestdebugA request has arrived
responsedebugA response has been sent
serverinfoThe server has started
serverinfoThe server has stopped
serverinfoThe server configuration has been reloaded
websocketdebugA new websocket connection happened
websocketdebugA message was received
websocketdebugA websocket connection was closed
websocketdebugRejected a websocket connection to invalid path
websocketerrorAn error happened with a websocket connection

Additionally, you can filter log entries by level and type with the following settings:

[log]
level = 'warn' # Only produce log entries with level 'warn' or higher ('error' & 'fatal')
type = 'my-type' # Only produce log entries with type matching exactly 'my-type'

You can disable logging entirely for a given app by setting it to false in the config

log = false

Async Handlers

Nodecaf brings the useful feature of accepting async functions as route handlers with zero configuration. All rejections/error within your async handler will be gracefully handled by the same routine the deals with regular functions. You will be able to avoid callback hell without creating bogus adapters for your promises.

get('/my/thing',
    function({ res, next }){
        res.send('My regular function works!');
        next();
    },
    async function({ res }){
        await myAsyncThing();
        res.end('My async function works too!');s
    }
);

Error Handling

In Nodecaf, any uncaught synchronous error happening inside route handler will be automatically converted into a harmless RESTful 500.

post('/my/thing', function(){
    throw new Error('Should respond with a 500');
});

To support the callback error pattern, use the res.error() function arg. This function will stop the middleware chain from being executed any further.

const fs = require('fs');

post('/my/thing', function({ res }){
    fs.readFile('./my/file', 'utf8', function(err, contents){
        if(err)
            return res.error(err);
        res.end(contents);
    });
});

To use other HTTP status codes you can send an integer in the first parameter of res.error().

post('/my/thing', function({ error }){
    try{
        doThing();
    }
    catch(e){
        error(404, 'Optional message for the response');
    }
});

REST Assertions

Nodecaf provides you with an assertion module containing functions to generate the most common REST outputs based on some condition. Check an example to trigger a 404 in case a database record doesn't exist.

get('/my/thing/:id', function({ params, db, res }){
    let thing = await db.getById(params.id);
    res.notFound(!thing, 'thing not found');

    doStuff();
});

If the record is not found, the res.notfound() call will stop the route execution right away and generate a RESTful NotFound error.

Along with notFound, the following assertions with similar behavior are provided:

MethodStatus Code
badRequest400
unauthorized401
forbidden403
notFound404
conflict409
gone410

Expose Globals

Nodecaf makes it simple to share global objects (eg.: database connections, instanced libraries) across all route handlers. In your lib/main.js you can expose an object of which all keys will become handler args.

module.exports = () => new Nodecaf({
    startup({ global }){
        global.db = myDbConnection;
        global.libX = new LibXInstance();
    }    
});

Then in all routes you can:

get('/my/thing', function({ db, libX }){
    // use your global stuff
});

CORS

Nodecaf provides a setting to enable permissive CORS on all routes. Defaults to disabled. In your conf file:

cors = true
cors = 'my://origin'
cors = [ 'my://origin1', 'my://origin2' ]

Setup the cors according to the popular CORS Express middleware.

HTTPS

In production it's generally desirable to have an HTTPS setup for both client to API and API to API communications. You can enable SSL for your server by adding a ssl key to your config, containing both the path for your key and cert.

[ssl]
key = "/path/to/key.pem"
cert = "/path/to/cert.pem"

When SSL is enabled the default server port becomes 443.

WebSocket Routes

With nodecaf you can define paths to be accessible as WebSocket endpoints.

In your api file use the ws(path, events) method with the folling arguments:

  1. path: where the websocket will be accessible
  2. events: object containing any of the following handlers: connect, message, close, error.
module.exports = function({ post, get, del, head, patch, put, ws }){

    // Regular api routes...

    // Websocket routes

    ws('/my-path-1', {
        connect: ({ client }) => console.log('NEW CLIENT'),
        message: ({ message, client }) => console.log('NEW MESSAGE'),
        close: ({ client }) => console.log('BYE CLIENT'),
        error: ({ err, client }) => console.log('I FEEL ODD')
    });

    ws('/my-path-2', {
        connect: ({ client }) => console.log('NEW CLIENT 2'),
        message: ({ message, client }) => console.log('NEW MESSAGE 2'),
        close: ({ client }) => console.log('BYE CLIENT 2'),
        error: ({ err, client }) => console.log('I FEEL ODD 2')
    });

};

All handlers are optional for each websocket endpoint.

Besides the ones showing, the following handler args are present in all ws handlers: client, req, flash, conf, log, headers, query and exposed vars.

Filter Requests by Mime-type

Nodecaf allow you to reject request bodies whose mime-type is not in a defined white-list. Denied requests will receive a 400 response with the apporpriate message.

Define a filter for the entire app on your api.js:

module.exports = function({ }){

    this.accept(['json', 'text/html']);

}

Override the global accept per route on your api.js:

const { accept } = require('nodecaf');

module.exports = function({ post, put }){

    // Define global accept rules
    this.accept(['json', 'text/html']);

    // Obtain accepts settings
    let json = accept('json');
    let img = accept([ 'png', 'jpg', 'svg', 'image/*' ]);

    // Prepend accept definition in each route chain
    post('/my/json/thing', json, myJSONHandler);
    post('/my/img/thing', img, myImageHandler);
}

API Description

Nodecaf allows you to descibe your api and it's functionality, effectively turning your code in the single source of truth. The described API can later be used to generate an Open API compatible document.

In lib/api.js describe your API as whole through the info parameter:

module.exports = function({ get, info }){

    info({
        description: 'My awesome API that foos the bars and bazes the bahs'
    });

    get('/my/thing/:id', function(){
        // ...
    });
}

The info funciton expects an object argument on the OpenAPI Info Object format. If not defined the title and version keys will default to your server's.

Describe your API endpoints by chaining a desc method to each route definition.

module.exports = function({ get }){

    get('/my/thing/:id', function(){
        // ...
    }).desc('Retrieves a thing from the database\n' +
        `Searches the database for the thing with the given :id. Returns a
        NotFound error in case no thing is found.`);
}

The desc method takes a single string argument and uses it's first line (before \n) to set the Operation object's summary property and the rest of the text to set the description (CommonMark).

Other Settings

PropertyTypeDescriptionDefault
app.conf.delayIntegerMilliseconds to wait before actually starting the app0
app.conf.portIntegerPort for the web server to listen (also exposed as user conf)80 or 443
app.conf.formFileDirPathWhere to store files uploaded as form-dataOS default temp dir
app.conf.cookie.secretStringA secure random string to be used for signing cookiesnone
opts.shouldParseBodyBooleanWether supported request body types should be parsedtrue
opts.alwaysRebuildAPIBooleanWether the API should be rebuilt dynamically for every start or setup operationfalse

Keywords

FAQs

Package last updated on 07 Aug 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc