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

restana

Package Overview
Dependencies
Maintainers
1
Versions
97
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

restana - npm Package Compare versions

Comparing version 3.4.2 to 4.0.0

51

index.d.ts

@@ -45,2 +45,14 @@ import { Server as HttpServer, IncomingMessage, ServerResponse } from 'http'

interface Router <P extends Protocol> {
get: RegisterRoute<P>
delete: RegisterRoute<P>
patch: RegisterRoute<P>
post: RegisterRoute<P>
put: RegisterRoute<P>
head: RegisterRoute<P>
options: RegisterRoute<P>
trace: RegisterRoute<P>
all: RegisterRoute<P>
}
type Response<P extends Protocol> = P extends Protocol.HTTP2

@@ -78,3 +90,2 @@ ? Http2ServerResponse & ResponseExtensions

handler: RequestHandler<P>,
context?: {},
middlewares?: RequestHandler<P>[]

@@ -87,3 +98,2 @@ ): Service<P>

handler: RequestHandler<P>,
context?: {}
): Service<P>

@@ -96,3 +106,2 @@

handler: RequestHandler<P>,
context?: {}
): Service<P>

@@ -106,3 +115,2 @@

handler: RequestHandler<P>,
context?: {}
): Service<P>

@@ -115,47 +123,24 @@ }

handler: RequestHandler<P>
ctx: {}
middlewares: RequestHandler<P>[]
}
interface Router { }
interface Options<P extends Protocol> {
server?: Server<P>
routerFactory?(options: Options<P>): Router
prioRequestsProcessing?: boolean
ignoreTrailingSlash?: boolean
allowUnsafeRegex?: boolean
maxParamLength?: number
routerCacheSize?: number
defaultRoute?: RequestHandler<P>
disableResponseEvent?: boolean
errorHandler?: ErrorHandler<P>
}
interface Service<P extends Protocol> {
interface Service<P extends Protocol> extends Router<P> {
getRouter(): Router<P>,
newRouter(): Router<P>
errorHandler: ErrorHandler<P>,
getServer(): Server<P>,
getConfigOptions(): Options<P>
use(middleware: RequestHandler<P>, context?: {}): void
route(
method: Method,
path: string,
handler: RequestHandler<P>,
ctx?: {},
middlewares?: RequestHandler<P>[]
): Route<P>
use(middleware: RequestHandler<P>): restana.Service<P>
use(prefix: string, middleware: RequestHandler<P>): restana.Service<P>
handle(req: Request<P>, res: Response<P>): void
start(port?: number, host?: string): Promise<Server<P>>
close(): Promise<void>
routes(): string[]
addRoute (methods: String | Array<String>): RegisterRoute<P>
get: RegisterRoute<P>
delete: RegisterRoute<P>
patch: RegisterRoute<P>
post: RegisterRoute<P>
put: RegisterRoute<P>
head: RegisterRoute<P>
options: RegisterRoute<P>
trace: RegisterRoute<P>
all: RegisterRoute<P>
}

@@ -162,0 +147,0 @@ }

@@ -7,13 +7,4 @@ /**

/**
* importing supported HTTP methods
*/
const methods = require('./libs/methods')
/**
* importing request router builder function
*/
const shortcuts = ['get', 'delete', 'patch', 'post', 'put', 'head', 'options', 'trace', 'all']
const requestRouter = require('./libs/request-router')
/**
* preparing request/response objects extensions
*/
const exts = {

@@ -23,26 +14,7 @@ request: {},

}
/**
* importing middlewares chain caller
*/
const next = require('./libs/middleware-chain')
/**
* importing route handler caller
*/
const handlerCall = require('./libs/route-handler-caller')
/**
* importing route registration handler
*/
const routeRegister = require('./libs/route-register')
/**
* Application instance contructor like function
*
* @param options Object Configuration options
*/
module.exports = (options = {}) => {
// create HTTP server instance
const router = requestRouter(options)
const server = options.server || require('http').createServer()
// should we prio requests processing?
const prp = undefined === options.prioRequestsProcessing ? true : options.prioRequestsProcessing
// registering 'request' handler
if (prp) {

@@ -58,29 +30,19 @@ server.on('request', (req, res) => {

// creating request router instance, considers custom router override
const router = options.routerFactory ? options.routerFactory(options) : requestRouter(options)
// routes holder
const routes = {}
const app = {
getRouter () {
return router
},
// global middlewares holder
const middlewares = [{
handler: (req, res, next) => router.lookup(req, res),
context: {}
}]
errorHandler: options.errorHandler || ((err, req, res) => {
res.send(err)
}),
// the "restana" service interface
const app = {
/**
* Application global error handler
*/
errorHandler: options.errorHandler || ((err, req, res) => res.send(err)),
newRouter () {
return requestRouter(options)
},
/**
* HTTP server instance
*/
getServer () {
return server
},
/**
* Application configuration options reference
*/
getConfigOptions () {

@@ -90,102 +52,11 @@ return options

/**
* Register global middleware
*
* @param {Object} middleware The middleware function
* @param {Object} context The middleware invokation context object
*/
use: (middleware, context = {}) => {
middlewares.splice(
middlewares.length - 1,
0,
{ handler: middleware, context }
)
},
use: router.use,
/**
* Register a request handler.
* Optionally the invokation context and pre-handler middlewares can be defined.
*
* @param {String} method HTTP method / verb
* @param {String} path Request path
* @param {Function} handler Request handler function like (req, res, ctx) => {}
* @param {Object} ctx Optional request handler invokation context object. Default: {}
* @param {Array} middlewares Optional request middlewares
* @returns {Object} Route object
*/
route: (method, path, handler, ctx = {}, middlewares = []) => {
// mapping middlewares to required format { handler: Function, context: Object }
middlewares = middlewares.map(
middleware => (typeof middleware === 'function')
? { handler: middleware, context: {} }
: middleware
)
// creating routing key
const key = `[${method.toString().toUpperCase()}]${path}`
// caching route
const route = {
method,
path,
handler,
ctx,
middlewares
}
routes[key] = true
// Allow override of routes, by first removing the old route
router.off(method, path)
// registering request handler
router.on(method, path, (req, res, params) => {
// populate req.params
req.params = params
if (middlewares.length > 0) {
// call route middlewares and route handler
return next([
...middlewares.slice(0),
{
context: {},
handler: handlerCall(handler, ctx, app.errorHandler) // -> Function
}
], req, res, app.errorHandler)
} else {
// directly call the route handler only
// NOTE: we do this to increase performance
return handlerCall(handler, ctx, app.errorHandler)(req, res)
}
})
return route
},
/**
* Handle on 'request' event from HTTP server instance
*
* @param {Object} req Request object
* @param {Object} res Response object
*/
handle: (req, res) => {
// request object population
req.originalUrl = req.url
res.send = exts.response.send(options, req, res)
if (middlewares.length > 1) {
// call route middlewares and route handler
next(middlewares, req, res, app.errorHandler)
} else {
// directly call the request router
// NOTE: we do this to increase performance
router.lookup(req, res)
}
router.lookup(req, res)
},
/**
* Start application HTTP server
*
* @param {Number} port Optional HTTP server port. Default 3000
* @param {String} host Optional HTTP server binding network interface
* @returns {Promise}
*/
start: (port = 3000, host) => new Promise((resolve, reject) => {

@@ -198,7 +69,2 @@ server.listen(port, host, (err) => {

/**
* Close application HTTP server
*
* @returns {Promise}
*/
close: () => new Promise((resolve, reject) => {

@@ -209,33 +75,21 @@ server.close((err) => {

})
}),
})
/**
* Application routes [`[${method.toUpperCase()}]${path}`]
*
* @returns {Array}
*/
routes: () => Object.keys(routes)
}
// exposing raw route registration to improve extensibility
app.addRoute = (methods) => (path, ...args) => {
routeRegister(app, methods, path, args)
shortcuts.forEach((method) => {
app[method] = router[method]
})
// supporting method chaining for routes registration
return app
}
app.callback = () => app.handle
// exposing "all" HTTP verbs as request routing registration
app.all = app.addRoute(methods)
// exposing HTTP verbs as request routing methods
// express.js like routes middlewares signature is supported: app.get('/', m1, m2, handler)
methods.forEach((method) => {
app[method] = app.addRoute(method)
app.use(async (req, res, next) => {
try {
await next()
} catch (err) {
return app.errorHandler(err, req, res)
}
})
// integrator callback
app.callback = () => app.handle
return app
}

@@ -17,3 +17,3 @@ const methods = require('./methods')

args.unshift((req, res, next) => {
apm.setTransactionName(`${method.toUpperCase()} ${path}`)
apm.setTransactionName(`${req.method} ${path}`)

@@ -20,0 +20,0 @@ return next()

/**
* Supported HTTP methods
*/
module.exports = ['get', 'delete', 'patch', 'post', 'put', 'head', 'options', 'trace']
module.exports = ['get', 'delete', 'patch', 'post', 'put', 'head', 'options', 'trace', 'all']

@@ -6,9 +6,13 @@ /**

*/
const router = require('find-my-way')
const sequential = require('0http/lib/router/sequential')
module.exports = (options) => router({
ignoreTrailingSlash: options.ignoreTrailingSlash || false,
allowUnsafeRegex: options.allowUnsafeRegex || false,
maxParamLength: options.maxParamLength || 100,
defaultRoute: options.defaultRoute || ((req, res) => res.send(404))
})
module.exports = (options) => {
const router = sequential({
cacheSize: options.routerCacheSize || 2000,
defaultRoute: options.defaultRoute || ((req, res) => {
res.send(404)
})
})
return router
}

@@ -6,12 +6,6 @@ const CONTENT_TYPE_HEADER = 'content-type'

* No comments needed ;)
*
* @param {Object} options Application configuration options
* @param {Object} req Request object
* @param {Object} res Response object
*/
module.exports.send = (options, req, res) => (data = 200, code = 200, headers = null, cb = () => {}) => {
if (headers !== null) {
// attach custom headers on the response
Object.keys(headers).forEach((key) => {
// IMPORTANT: 'key.toLowerCase()' give us big performance gain
res.setHeader(key.toLowerCase(), headers[key])

@@ -22,11 +16,8 @@ })

if (typeof data === 'number') {
// shortcut was used, check if data payload was set in res.body
code = parseInt(data, 10)
data = res.body
} else if (data instanceof Error) {
// transparently supporting Error instances
const errorCode = data.status || data.code || data.statusCode
code = typeof errorCode === 'number' ? parseInt(errorCode) : 500
data = {
errClass: data.constructor.name,
code,

@@ -36,29 +27,16 @@ message: data.message,

}
res.setHeader(CONTENT_TYPE_HEADER, 'application/json')
}
// emit response event to allow post-processing
// TODO: We need to make this event notification async without affecting performance
const params = {
res,
req,
data,
code
}
if (options.disableResponseEvent !== true) {
res.emit('response', params)
}
if (typeof data === 'object' && data instanceof Buffer === false) {
if (!res.hasHeader(CONTENT_TYPE_HEADER)) {
// transparently setting the 'content-type' header if JSON
res.setHeader(CONTENT_TYPE_HEADER, 'application/json')
}
params.data = JSON.stringify(params.data)
data = JSON.stringify(data)
}
// setting res.statusCode with post-processed result, developers might want to override here...
res.statusCode = params.code
res.statusCode = code
// finally end request
res.end(params.data, cb)
res.end(data, cb)
}
{
"name": "restana",
"version": "3.4.2",
"version": "4.0.0",
"description": "Super fast and minimalist web framework for building REST micro-services.",

@@ -10,3 +10,3 @@ "main": "index.js",

"format": "npx standard --fix",
"test": "PORT=3000 NODE_ENV=testing npx nyc --check-coverage --lines 95 node ./node_modules/mocha/bin/mocha tests.js specs/*.test.js",
"test": "PORT=3000 NODE_ENV=testing npx nyc --check-coverage --lines 95 node ./node_modules/mocha/bin/mocha specs/*.test.js",
"postinstall": "node ./libs/tasks/postinstall.js"

@@ -26,3 +26,3 @@ },

"engines": {
"node": ">=7.x"
"node": ">=10.x"
},

@@ -43,6 +43,5 @@ "author": "Rolando Santamaria Maso <kyberneees@gmail.com>",

"dependencies": {
"find-my-way": "^2.2.1"
"0http": "^2.1.0"
},
"devDependencies": {
"0http": "^1.2.4",
"@hapi/hapi": "^18.4.0",

@@ -53,4 +52,5 @@ "anumargak": "^2.2.0",

"express": "^4.17.1",
"express-jwt": "^5.3.1",
"fastify": "^2.11.0",
"http-cache-middleware": "^1.2.3",
"http-cache-middleware": "^1.2.4",
"koa": "^2.11.0",

@@ -61,8 +61,10 @@ "koa-router": "^7.4.0",

"muneem": "^2.4.5",
"nyc": "^14.1.1",
"nyc": "^15.0.0",
"pem": "^1.14.3",
"polka": "^0.5.2",
"response-time": "^2.3.2",
"restify": "^8.5.0",
"restify": "^8.5.1",
"serve-static": "^1.14.1",
"socket.io": "^2.3.0",
"socket.io-client": "^2.3.0",
"standard": "^14.3.1",

@@ -69,0 +71,0 @@ "supertest": "^3.4.2",

@@ -5,7 +5,9 @@ # restana

Blazing fast, tiny and minimalist *connect-like* web framework for building REST micro-services.
[> Check how much faster!](https://github.com/the-benchmarker/web-frameworks#full-table-1)
> Uses 'find-my-way' router: https://www.npmjs.com/package/find-my-way
What else? *[Building ultra-fast REST APIs with Node.js (restana vs express vs fastify)](https://medium.com/@kyberneees/building-ultra-fast-rest-apis-with-node-js-and-restana-1d65b0d524b7)*
![Performance Benchmarks](benchmark-30122019.png)
> MacBook Pro 2019, 2,4 GHz Intel Core i9, 32 GB 2400 MHz DDR4
> - wrk -t8 -c40 -d5s http://127.0.0.1:3000/hi
Read more: *[Building ultra-fast REST APIs with Node.js (restana vs express vs fastify)](https://medium.com/@kyberneees/building-ultra-fast-rest-apis-with-node-js-and-restana-1d65b0d524b7)*
## Usage

@@ -35,41 +37,11 @@ ```bash

### Configuration
- `server`: Allows to override the HTTP server instance to be used.
- `routerFactory`: Router factory function to allow default `find-my-way` router override.
### Configuration options
- `server`: Allows to optionally override the HTTP server instance to be used.
- `prioRequestsProcessing`: If `TRUE`, HTTP requests processing/handling is prioritized using `setImmediate`. Default value: `TRUE`
- `ignoreTrailingSlash`: If `TRUE`, trailing slashes on routes are ignored. Default value: `FALSE`
- `allowUnsafeRegex`: If `TRUE`, potentially catastrophic exponential-time regular expressions are disabled. Default value: `FALSE`
- `maxParamLength`: Defines the custom length for parameters in parametric (standard, regex and multi) routes. Default value: `100`
- `defaultRoute`: Default route handler when no route match occurs. Default value: `((req, res) => res.send(404))`
- `disableResponseEvent`: If `TRUE`, there won't be `response` events triggered on the `res` object. Default value: `FALSE`
- `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`
```js
// accessing service configuration
service.getConfigOptions()
// accessing restana HTTP server instance
service.getServer()
```
#### Example usage:
```js
const service = require('restana')({
ignoreTrailingSlash: true
});
```
#### Optionally overwrite router factory method:
> In this example we use `anumargak` router instead of `find-my-way`.
```js
const anumargak = require('anumargak')
const service = require('restana')({
routerFactory: (options) => {
return anumargak(options)
}
})
...
```
> Please consider that when using `anumargak` router, request params are accessible via: `req._path.params`
### Creating a micro-service & routes registration
### Full service example
```js

@@ -85,3 +57,3 @@ const bodyParser = require('body-parser')

// registering routes using method chaining
// registering service routes
service

@@ -105,8 +77,11 @@ .get('/pets/:id', async (req, res) => {

service.get('/version', function (req, res) {
res.body = { // optionally you can send the response data in the body property
// optionally you can send the response data in the body property
res.body = {
version: '1.0.0'
}
res.send() // 200 is the default response code
// 200 is the default response code
res.send()
})
```
Supported HTTP methods:

@@ -120,3 +95,3 @@ ```js

```js
service.all('/allmethodsroute', function (req, res) {
service.all('/allmethodsroute', (req, res) => {
res.send(200)

@@ -143,9 +118,6 @@ })

return stars
res.send({ stars })
})
```
> IMPORTANT: Returned value can't be `undefined`, for such cases use `res.send(...`
### Sending custom headers:

@@ -180,23 +152,22 @@ ```js

### Middlewares support:
### Global middlewares
```js
const service = require('restana')({})
const service = require('restana')()
// custom middleware to attach the X-Response-Time header to the response
service.use((req, res, next) => {
const now = new Date().getTime()
res.on('response', e => {
e.res.setHeader('X-Response-Time', new Date().getTime() - now)
})
return next()
// do something
next()
});
...
```
// the /v1/welcome route handler
service.get('/v1/welcome', (req, res) => {
res.send('Hello World!')
})
### Prefix middlewares
```js
const service = require('restana')()
// start the server
service.start()
service.use('/admin', (req, res, next) => {
// do something
next()
});
...
```

@@ -207,16 +178,14 @@

```js
service.get('/hi/:name', async (req, res) => {
return 'Hello ' + req.params.name // -> "name" will be uppercase here
}, {}, [(req, res, next) => {
req.params.name = req.params.name.toUpperCase()
const service = require('restana')()
service.get('/admin', (req, res, next) => {
// do something
next()
}]) // route middlewares can be passed in an Array after the handler context param
}, (req, res) => {
res.send('admin data')
});
...
```
Express.js like signature also supported:
```js
service.get('/hi/:name', m1, m2, handler [, ctx])
```
#### Third party middlewares support:
> Almost all middlewares using the *function (req, res, next)* signature format should work, considering that no custom framework feature is used.
> All middlewares using the `function (req, res, next)` signature format are compatible with restana.

@@ -230,13 +199,14 @@ Examples :

#### Async middlewares support
Starting from `v3.3.x`, you can now also use async middlewares as described below:
Since version `v3.3.x`, you can also use async middlewares as described below:
```js
service.use(async (req, res, next) => {
await next()
console.log('Global middlewares execution completed!')
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 your async middlewares:
In the same way you can also capture uncaught exceptions inside the request processing flow:
```js

@@ -254,4 +224,2 @@ service.use(async (req, res, next) => {

```
> NOTE: Global and Route level middlewares execution run separately!
## AWS Serverless Integration

@@ -335,3 +303,17 @@ `restana` is compatible with the [serverless-http](https://github.com/dougmoscrop/serverless-http) library, so restana based services can also run as AWS lambdas 🚀

## Breacking changes
### 4.x:
> Restana version 4.x is much more simple to maintain, mature and faster!
#### Added
- 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` and `newRouter` methods are added for accesing default and nested routers.
#### Removed
- The `response` event was removed.
- `find-my-way` router is replaced by `0http` sequential router.
- Returning result inside async handler is not allowed anymore. Use `res.send...`
### 3.x:
#### Removed
- Support for `turbo-http` library was dropped.
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