A modular System-API framework for NodeJS - on top of RestifyJS.
It is written in Coffeescript and compiled to Javascript.
- Authorization (optional bcrypt, anonymous)
- CORS (Cross-Origin Resource Sharing)
- HTTP/S (TLS)
- Validation Engine
- Body Parsers (JSON, URL, Multipart)
- JSON Body Parser
- Body Parser
- Accept Parser
- Date Parser
- Query Parser
- Jsonp
- Gzip Response / Compression
- Request Expiry
- Throttle
- Conditional Request
- Audit Logger
- Sanitize Path
- Serve Static
- Extensive Routing
- Logging (morgan/custom)
- Plugins (extend your api instance)
- Addons (extend sys-api's core)
- Routing
- Authorization
- CORS (Cross-Origin Resource Sharing)
- HTTP/S - TLS
- Validation
- BodyParser
- Additional Restify plugins
- Plugins
- Core-Addons
- Demos / Examples
- How to install
- Unit-Tests
- Contributing
- License
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
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.
api.auth({
enabled: true,
method: 'basic',
bcrypt: false,
anon: false,
users: {
testuser: {
password: 'testpw'
}
}
})
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[...]
}
}
})
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'
}
}
})
api.cors({
enabled: true,
origins: ['https://foo.com', 'http://bar.com'], # defaults to ['*']
credentials: true, # defaults to false
headers: ['x-foo'] # sets expose-headers
})
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.
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))
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'
})
In addition to the BodyParser, the following plugins are available:
- Accept -
api.acceptParser()
- Date Parser -
api.dateParser()
- Query Parser -
api.queryParser()
- Jsonp -
api.jsonp()
- Gzip Response / Compression -
api.gzipResponse()
- Request Expiry -
api.requestExpiry()
- Throttle -
api.throttle()
- Conditional Request -
api.conditionalRequest()
- Audit Logger -
api.auditLogger()
- Request Logger -
api.requestLogger()
- Sanitize Path -
api.sanitizePath()
- Serve Static -
api.serveStatic()
- Full Response -
api.fullResponse()
- JSON Body Parser -
api.jsonBodyParser()
- Multipart Body Parser -
api.multipartBodyParser()
- URL Encoded Body Parser -
api.urlEncodedBodyParser()
Documentation: Restify Bundled 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 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.
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
Just use npm install @burnett01/sys-api
and use the content of the demo-file.
If you experience any problems try using npm install @burnett01/sys-api --no-bin-links
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.
- test/test.default.js
Performs 8 tests
| Source
Output using Mocha spec
reporter:
Default reporter: list
make test
npm test
You're very welcome and free to contribute. Thank you.