sys-api

A modular System-API framework for NodeJS - on top of RestifyJS.

It is written in Coffeescript and compiled to Javascript.

Build Status npm version


Features:


Table of contents


Routing

You can use a route in many different ways!

For example, this is how simple it can be:

api.get('/hello', "Hello World")
#=> {"response":"Hello World"}
api.get('/hello', (router) ->
    router.send("Hello World")
)
#=> {"response":"Hello World"}
api.get('/hello', (router) ->
    router.res.send("Hello World")
    return router.next()
)
#=> "Hello World"

You can also use an object:

api.get({ url: '/hello' }, "Hello World")
#=> {"response":"Hello World"}

Check the wiki for more: Go to Wiki


Authorization

Such as Restify, currently only HTTP Basic Auth and HTTP Signature are supported.

In addition, we allow bcyrpt encrypted passwords to be used in Basic Auth HTTP header, and anonymous access.

Basic example:

api.auth({
    enabled: true,
    method: 'basic',    
    bcrypt: false,
    anon: false,
    users: {
        testuser: {
            password: 'testpw'
        }   
    }
})

Bcrypt example:

if bcrypt is enabled, pass the hash with the Basic Auth header.

api.auth({
    enabled: true,
    method: 'basic',
    bcrypt: true,
    anon: false,
    users: {
        testuser: {
            # Whenever bcrypt is enabled, we must use the hash instead of the plain password.
            #
            # For example:
            #   Let's say the plain password "testpw" becomes this bcrypt hash: 
            #   "$2a$04$GRD1gvo20Gqskwk5g9qsgO0urOWDAO[...]"
            # ---------------------------------------------------------------------
            # So we must use the hash:
            password: '$2a$04$GRD1gvo20Gqskwk5g9qsgO0ur[...]'
            # ---------------------------------------------------------------------
            # Now our application (for instance PHP) generates a new hash
            # to be used in authorization procedure.
            # As soon as the application wants to perform authorization against the API,
            # the Basic Auth header must contain the hash in its base64 representation:
            # ---------------------------------------------------------------------
            # This is how a generic authorization header looks like:
            #   username:password
            #   -> test:testpw
            #   -> base64
            #   -> dGVzdDp0ZXN0cHc=
            #   ++ So the Authorization header becomes:
            #   Authorization: Basic dGVzdDp0ZXN0cHc=
            # ---------------------------------------------------------------------
            # This is how a the authorization header with bcrypt may look like:
            #   username:hash
            #   -> test:$2a$04$jdGtS8OCXCn.e2b1DI584OAA65r0[...]
            #   -> base64
            #   -> dGVzdDokMmEkMDQkamRHdFM4T0NYQ24uZTJiMURJNTg0T0FBNjV[...]
            #   ++ So the Authorization header becomes:
            #   Authorization: Basic dGVzdDokMmEkMDQkamRHdFM4T0NYQ24uZTJiMURJNTg0T0FBNjV[...]
        }   
    }
})

Anonymous access:

You may also allow anonymous access by using the anon property.

If anonymous access is enabled, valid and anonymous users have access.

api.auth({
    enabled: true,
    method: 'basic',    
    bcrypt: false,
    anon: true,
    users: {
        testuser: {
            password: 'testpw'
        }   
    }
})

CORS (Cross-Origin Resource Sharing)

api.cors({
    enabled: true,
    origins: ['https://foo.com', 'http://bar.com'],  # defaults to ['*']
    credentials: true,  # defaults to false
    headers: ['x-foo']  # sets expose-headers
})

HTTP/S - TLS

Sys-API supports HTTP and HTTPS simultaneously.

You don’t have to define things twice. Simply pass a key and certificate property, and the API will handle that for you. Once configured, your API-instance will listen on your specified HTTP and HTTPS port. Port 443 is the default port for HTTPS. If you wish to use any other port, simply pass a second argument to listen().

api = new API({
    restify: {
        key: readFileSync('localhost.key'),
        certificate: readFileSync('localhost.cert')
    }
})

api.listen(80) #API is going to listen on HTTP(80) and HTTPS(443)

# OR

api.listen(80, 8443) #API is going to listen on port HTTP(80) and HTTPS(8443)

If no key/certificate property is available, your API-instance won’t support HTTPS.


Validation

The validator is based on restify-validation-engine and instead of including restify-validation-engine as a dependency,
I copied the code and updated it to support new features:

Simply enable the validator by using:

api.validator({
    enabled: true
})

To use custom validators simply issue:

api.validator({
    enabled: true,
    customValidators : {
        ......
    }
})

After enabling it, you may use the validate: property in your routes:

api.get({
    url: '/test',
    validate: { 
        params: { 
            name: {
                required: true
            }
        }
    }}, r => r.send('Passed validation))

You can find the validation documentation at this repository

These are my additional features that the validator-engine supports:

+ The validate property now allows an array of scope objects so
that you can fully utilize ES6 spread features:

const validators = {
    name: {
        required: true 
    },
    other: {
        .....
    }
}

api.get({
    url: '/test',
    validate: [ 
        { body: { ...validators }},
        { params: { ...... } }
    ]}, r => {
        r.next
        r.send
        r.req
        r.res
    })

+ The req object is now forwarded to custom validators.
This allows you to use custom validation functions as middleware.

const myValidator = (name, req) => {
    // do something with req
    // ....
    // check for valid name
    return (name == 'cool')
}

api.validator({
    enabled: true,
    customValidators: {
        myValidator
    }
})

api.get({
    url: '/test',
    validate: { 
        params: { 
            name: {
                required: true,
                myValidator: 'Invalid name'
            }
        }
    }}, r => r.send('Passed validation))

BodyParser

The BodyParser can be enabled with a single option. Everything else is handled for you. Once enabled, you can access the body with obj.req.body in your routes.

Check: https://github.com/Burnett01/sys-api/blob/master/examples/example.coffee#L53

api.bodyParser({
    enabled: true
})

If you want to change more settings:

api.bodyParser({
    enabled: true,
    maxBodySize: 0,
    mapParams: true,
    mapFiles: false,
    overrideParams: false,
    multipartHandler: function(part) {
        part.on('data', function(data) {
            # do something with the multipart data
        });
    },
    multipartFileHandler: function(part) {
        part.on('data', function(data) {
            #do something with the multipart file data
        });
    },
    keepExtensions: false,
    uploadDir: os.tmpdir(),
    multiples: true
    hash: 'sha1'
})

Additional Restify plugins

In addition to the BodyParser, the following plugins are available:

Documentation: Restify Bundled Plugins


Plugins

As of version 0.2.0 you can create your own plugins. They allow you to extend your API without changing Sys-API’s source.

Plugins can also act as middlware:

api = new API({
    'plugins.root' : '/home/plugins'
    'plugins.autoload' : true
})

api.get('/myplugin/test', api.myplugin.test)

api.listen(8000)

Check out: https://github.com/Burnett01/sys-api/wiki/Create-a-plugin

Check out: https://github.com/Burnett01/sys-api/wiki/Routing


Core-Addons

Core-Addons are bound to the API and core-features. Such as plugins, they can be maintained from within an external file. As of now there are three Core-Addons available (FS, OS, NET) but you can create you own. They can also act as middleware.

Check out the wiki for instructions:

https://github.com/Burnett01/sys-api/wiki/Create-an-Addon-(core)

Once you’ve finished your addon, please submit a pull-request. If it’s useful, it’ll be added.


Demos / Examples:

You should definately check the examples:

Coffeescript: https://github.com/Burnett01/sys-api/blob/master/examples/example.coffee

Javascript: https://github.com/Burnett01/sys-api/blob/master/examples/example.js


How to install:

Just use npm install @burnett01/sys-api and use the content of the demo-file.

Support

If you experience any problems try using npm install @burnett01/sys-api --no-bin-links


Unit-Tests

The testing-framework used by this module is Mocha with the BDD / TDD assertion library Chai.

Various tests are performed to make sure this module runs as smoothly as possible.

Output using Mocha spec reporter:

Default reporter: list

Make

make test

NPM

npm test


Contributing

You’re very welcome and free to contribute. Thank you.


License

MIT