Code Monkey home page Code Monkey logo

restana's Introduction

restana

Build Status NPM version
Blazing fast, tiny and minimalist connect-like web framework for building REST micro-services.

Performance Benchmarks

MacBook Pro 2019, 2,4 GHz Intel Core i9, 32 GB 2400 MHz DDR4

Read more: restana = faster and efficient Node.js REST APIs

Usage

npm i restana --save

Creating the service instance

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 by http.createServer.

See examples:

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
  • 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

Full service example

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']

Using .all routes registration

You can also register a route handler for all supported HTTP methods:

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

Starting the service

service.start(3000).then((server) => {})

Stopping the service

service.close().then(()=> {})

Async / Await support

// 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 })
})

Sending custom headers

res.send('Hello World', 200, {
  'x-response-time': 100
})

The "res.send" method

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!'))
})

The method signature

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)

Global error handling

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!')
})

errorHandler not being called?

Issue: BackendStack21#81

Some middlewares don't do return next(), instead they just call next() to finish and continue the remaining middlewares execution. The second, is a bad practice as it silence any potential Promise rejection that happens in the downstream middlewares or handlers.

In restana (https://github.com/jkyberneees/ana/blob/master/index.js#L99) we enable async errors handling by default, however this mechanism fails when a subsequent middleware is registered containing the mentioned next() statement to finish their execution.

Global middlewares

const service = require('restana')()

service.use((req, res, next) => {
  // do something
  return next()
});
...

Prefix middlewares

const service = require('restana')()

service.use('/admin', (req, res, next) => {
  // do something
  return next()
});
...

Route level middlewares

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

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

Third party middlewares support:

All middlewares using the function (req, res, next) signature format are compatible with restana.

Examples :

Async middlewares support

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())

AWS Serverless Integration

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

Cloud Functions for Firebase Integration

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());

Serving static files

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:

Third party integrations

// ...
const service = restana()
service.get('/hello', (req, res) => {
  res.send('Hello World!')
})

// using "the callback integrator" middleware
const server = http.createServer(service.callback())
//...

Application Performance Monitoring (APM)

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:

Elastic APM - Routes Naming

"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!')
})

// ...

New Relic - Routes Naming

"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!')
})

// ...

Performance comparison (framework overhead)

Which is the fastest?

You can checkout restana performance index on the "Which is the fastest" project: https://github.com/the-benchmarker/web-frameworks#full-table-1

Using this project? Let us know ๐Ÿš€

https://goo.gl/forms/qlBwrf5raqfQwteH3

Breaking 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.

Support / Donate ๐Ÿ’š

You can support the maintenance of this project:

  • Paypal: https://www.paypal.me/kyberneees
  • NANO Crypto Coin: nano_3zm9steh8mb374f8be3rbytqhgzzarczhwtxhihkqt83a4m46oa3xidfiauc
  • XRP Crypto Coin: rarQgNuiqF9gFLLwd5fdku4jYa9EXpiyCp
  • TRON Crypto Coin: TJ5Bbf9v4kpptnRsePXYDvnYcYrS5Tyxus
  • BITCOIN Crypto Coin: bc1qcrr58venyh54ztvkqym39p9rhnxg4308t0802f
  • Ethereum Crypto Coin: 0xD73c8E63a83eBD8Df3fB3d0090f1fe7a1eEB980B

restana's People

Contributors

0joshuaolson1 avatar adrianjost avatar akkuma avatar edreesjalili avatar jesusvilla avatar jkyberneees avatar kelvinfloresta avatar mrddter avatar normanzb avatar schamberg97 avatar silverwind avatar tomc974 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.