Blazing fast, tiny and minimalist connect-like web framework for building REST micro-services.
You can read more: restana = faster and efficient Node.js REST APIs
npm i restana --save
Create unsecure HTTP server:
const service = require('restana')()
Passing HTTP server instance:
const https = require('https')
const service = require('restana')({
server: https.createServer({
key: keys.serviceKey,
cert: keys.certificate
})
})
Create restana HTTP server with http.createServer()
:
const http = require('http')
const service = require('restana')()
service.get('/hi', (req, res) => {
res.send({
msg: 'Hello World!'
})
})
http.createServer(service).listen(3000, '0.0.0.0', function () {
console.log('running')
})
Please take note that in the last case,
service.close()
would not be available, since restana does not have access to http server instance created byhttp.createServer
.
Optionally, learn through examples:
server
: Allows to optionally override the HTTP server instance to be used.prioRequestsProcessing
: IfTRUE
, HTTP requests processing/handling is prioritized usingsetImmediate
. Default value:TRUE
defaultRoute
: Optional route handler when no route match occurs. Default value:((req, res) => res.send(404))
errorHandler
: Optional global error handler function. Default value:(err, req, res) => res.send(err)
routerCacheSize
: The router matching cache size, indicates how many request matches will be kept in memory. Default value:2000
const bodyParser = require('body-parser')
const service = require('restana')()
service.use(bodyParser.json())
const PetsModel = {
// ...
}
// registering service routes
service
.get('/pets/:id', async (req, res) => {
res.send(await PetsModel.findOne(req.params.id))
})
.get('/pets', async (req, res) => {
res.send(await PetsModel.find())
})
.delete('/pets/:id', async (req, res) => {
res.send(await PetsModel.destroy(req.params.id))
})
.post('/pets/:name/:age', async (req, res) => {
res.send(await PetsModel.create(req.params))
})
.patch('/pets/:id', async (req, res) => {
res.send(await PetsModel.update(req.params.id, req.body))
})
service.get('/version', function (req, res) {
// optionally you can send the response data in the body property
res.body = {
version: '1.0.0'
}
// 200 is the default response code
res.send()
})
Supported HTTP methods:
const methods = ['get', 'delete', 'put', 'patch', 'post', 'head', 'options', 'trace']
You can also register a route handler for all
supported HTTP methods:
service.all('/allmethodsroute', (req, res) => {
res.send(200)
})
service.start(3000).then((server) => {})
service.close().then(()=> {})
// some fake "star" handler
service.post('/star/:username', async (req, res) => {
await starService.star(req.params.username)
const stars = await starService.count(req.params.username)
res.send({ stars })
})
res.send('Hello World', 200, {
'x-response-time': 100
})
Same as in express, for restana
we have implemented a handy send
method that extends
every res
object.
Supported datatypes are:
- null
- undefined
- String
- Buffer
- Object
- Stream
- Promise
Example usage:
service.get('/promise', (req, res) => {
res.send(Promise.resolve('I am a Promise object!'))
})
res.send(
// data payload
'Hello World',
// response code (default 200)
200,
// optional response headers (default NULL)
{
'x-cache-timeout': '5 minutes'
},
// optional res.end callback
err => { /*...*/ }
)
Optionally, you can also just send a response code:
res.send(401)
const service = require('restana')({
errorHandler (err, req, res) {
console.log(`Something was wrong: ${err.message || err}`)
res.send(err)
}
})
service.get('/throw', (req, res) => {
throw new Error('Upps!')
})
Issue: BackendStack21#81
Some middlewares don't call return next()
inside a synchronous flow. In restana we enable async errors handling by default, however this mechanism fails when a subsequent middleware is just calling next()
inside a sync or async flow.
Known incompatible middlewares:
- body-parser (https://www.npmjs.com/package/body-parser)
How to bring async chain compatibility to existing middlewares? The body-parser
example:
const jsonParser = require('body-parser').json()
const service = require('restana')()
service.use((req, res, next) => {
return new Promise(resolve => {
jsonParser(req, res, (err) => {
return resolve(next(err))
})
})
})
const service = require('restana')()
service.use((req, res, next) => {
// do something
return next()
});
...
const service = require('restana')()
service.use('/admin', (req, res, next) => {
// do something
return next()
});
...
Connecting middlewares to specific routes is also supported:
const service = require('restana')()
service.get('/admin', (req, res, next) => {
// do something
return next()
}, (req, res) => {
res.send('admin data')
});
...
As well, multiple middleware callbacks are supported:
const service = require('restana')()
const cb0 = (req, res, next) => {
// do something
return next()
}
const cb1 = (req, res, next) => {
// do something
return next()
}
service.get('/test/:id', [cb0, cb1], (req, res) => {
res.send({ id: req.params.id })
})
Nested routers are supported as well:
const service = require('restana')()
const nestedRouter = service.newRouter()
nestedRouter.get('/hello', (req, res) => {
res.send('Hello World!')
})
service.use('/v1', nestedRouter)
...
In this example the router routes will be available under /v1
prefix. For example: GET /v1/hello
All middlewares using the
function (req, res, next)
signature format are compatible with restana.
Examples :
- raw-body: https://www.npmjs.com/package/raw-body. See demo: raw-body.js
- express-jwt: https://www.npmjs.com/package/express-jwt. See demo: express-jwt.js
- body-parser: https://www.npmjs.com/package/body-parser. See demo: body-parser.js
Since version v3.3.x
, you can also use async middlewares as described below:
service.use(async (req, res, next) => {
await next()
console.log('All middlewares and route handler executed!')
}))
service.use(logging())
service.use(jwt())
...
In the same way you can also capture uncaught exceptions inside the request processing flow:
service.use(async (req, res, next) => {
try {
await next()
} catch (err) {
console.log('upps, something just happened')
res.send(err)
}
})
service.use(logging())
service.use(jwt())
Service events are accessible through the service.events
object, an instance of https://nodejs.org/api/events.html
service.events.BEFORE_ROUTE_REGISTER
: This event is triggered before registering a route.
restana
is compatible with the serverless-http library, so restana based services can also run as AWS lambdas 🚀
// required dependencies
const serverless = require('serverless-http')
const restana = require('restana')
// creating service
const service = restana()
service.get('/hello', (req, res) => {
res.send('Hello World!')
})
// lambda integration
const handler = serverless(app);
module.exports.handler = async (event, context) => {
return await handler(event, context)
}
See also:
Running restana service as a lambda using AWS SAM at https://github.com/jkyberneees/restana-serverless
restana
restana based services can also run as Cloud Functions for Firebase 🚀
// required dependencies
const functions = require("firebase-functions");
const restana = require('restana')
// creating service
const service = restana()
service.get('/hello', (req, res) => {
res.send('Hello World!')
})
// lambda integration
exports = module.exports = functions.https.onRequest(app.callback());
You can read more about serving static files with restana in this link: https://itnext.io/restana-static-serving-the-frontend-with-node-js-beyond-nginx-e45fdb2e49cb
Also, the restana-static
project simplifies the serving of static files using restana and docker containers:
// ...
const service = restana()
service.get('/hello', (req, res) => {
res.send('Hello World!')
})
// using "the callback integrator" middleware
const server = http.createServer(service.callback())
//...
As a Node.js framework implementation based on the standard http
module, restana
benefits from out of the box instrumentation on
existing APM agents such as:
"Routes Naming" discovery is not supported out of the box by the Elastic APM agent, therefore we have created our custom integration.
// getting the Elastic APM agent
const agent = require('elastic-apm-node').start({
secretToken: process.env.APM_SECRET_TOKEN,
serverUrl: process.env.APM_SERVER_URL
})
// creating a restana application
const service = require('restana')()
// getting restana APM routes naming plugin
const apm = require('restana/libs/elastic-apm')
// attach route naming instrumentation before registering service routes
apm({ agent }).patch(service)
// register your routes or middlewares
service.get('/hello', (req, res) => {
res.send('Hello World!')
})
// ...
"Routes Naming" discovery is not supported out of the box by the New Relic APM agent, therefore we have created our custom integration.
// getting the New Relic APM agent
const agent = require('newrelic')
// creating a restana application
const service = require('restana')()
// getting restana APM routes naming plugin
const apm = require('restana/libs/newrelic-apm')
// attach route naming instrumentation before registering service routes
apm({ agent }).patch(service)
// register your routes or middlewares
service.get('/hello', (req, res) => {
res.send('Hello World!')
})
// ...
You can checkout restana
performance index on the "Which is the fastest" project: https://github.com/the-benchmarker/web-frameworks#full-table-1
https://goo.gl/forms/qlBwrf5raqfQwteH3
Restana version 4.x is much more simple to maintain, mature and faster!
- Node.js v10.x+ is required.
0http
sequential router is now the default and only HTTP router.- Overall middlewares support was improved.
- Nested routers are now supported.
- Improved error handler through async middlewares.
- New
getRouter
andnewRouter
methods are added for accesing default and nested routers.
- The
response
event was removed. find-my-way
router is replaced by0http
sequential router.- Returning result inside async handler is not allowed anymore. Use
res.send...
- Support for
turbo-http
library was dropped.
You can support the maintenance of this project:
- PayPal: https://www.paypal.me/kyberneees
- TRON Wallet:
TJ5Bbf9v4kpptnRsePXYDvnYcYrS5Tyxus