Fast & minimalist
REST framework for Node.js

Blazing fast, tiny, and secure-by-default. Express-inspired API with serverless support, nested routers, and zero bloat.

7
HTTP methods
v7.0
latest
Node 24+
runtime
1
dependency
TS
types built-in

Faster routing, consistent errors

Restana 7.0 switches to a dependency-free trie router and tightens the public API. Upgrading from 6.x? Read the migration guide.

Trie router

Static routes resolve in constant time and dynamic routes stay fast as the route count grows, without a match cache to tune.

Case-sensitive matching

Paths match the way reverse proxies and WAFs classify them. Opt out with caseSensitive: false, or keep the sequential router.

Consistent errors

The default error handler and res.send(err) share one masked response format with standard status texts.

Explicit public API

An exports map, typed APM integrations, and router-returning chaining in the TypeScript declarations.

Typed route params

req.params is inferred from the route path: '/users/:id' gives you req.params.id, with no annotations needed.

Faster startup

The sequential router loads only when selected, so require('restana') is about 40% faster and pulls in only 0http's trie by default.

Correct responses

Explicit status codes survive empty and promise bodies, string error codes no longer mask err.statusCode, and coverage is held at 100%.

Why restana?

A framework designed for speed, simplicity, and production safety.

⚡

Blazing Fast

Lightweight routing with minimal overhead. The default trie router resolves static routes in constant time and needs no match cache — tuned for raw throughput.

🛡

Secure by Default

Default error masking, header injection protection, security headers, TRACE disabled, and isolated config snapshots — all on by default.

🔗

Middleware Engine

Express-compatible middleware with full async/await support. Global, prefix, and route-level — you choose the scope.

🧩

Nested Routers

Modular architecture with nested routers via service.newRouter(). Mount under prefixes and compose cleanly.

☁️

Serverless Ready

Native integration with AWS Lambda via serverless-http and Google Cloud Functions for Firebase. Deploy anywhere.

📦

Minimal Footprint

One direct dependency, and the default trie router loads nothing beyond it. Zero bloat. The res.send() method handles null, strings, buffers, objects, streams, and promises — all in one call.

Safe Out of the Box

restana v7.0 ships with hardened security defaults. No extra packages needed.

🔒

Safe Error Masking

Error details are hidden in every environment by default. Local development can explicitly opt in with debugErrors: true; production always masks details.

🚫

Header Injection Protection

Security-sensitive and hop-by-hop headers (transfer-encoding, content-length, connection, set-cookie, etc.) are silently dropped from res.send(). Invalid header characters are caught, not crashed.

🛡

Default Security Headers

Every response gets X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and X-XSS-Protection: 0. HSTS is set for direct TLS or explicitly trusted proxies.

🔐

TRACE Disabled by Default

Eliminates Cross-Site Tracing (XST) attack surface. Re-enable for debugging with enableTrace: true — not recommended in production.

❄️

Immutable Configuration

getConfigOptions() returns an isolated snapshot. Nested plain objects and arrays are cloned and frozen so middleware cannot mutate framework options.

🌊

Stream & Promise Safety

Stream errors are handled gracefully (connection terminates, no leak). Promise resolution is capped at depth 3 to prevent event loop starvation.

Quick Start

Common patterns to get you moving fast.

basic-server.js
const restana = require('restana')

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

service.start(3000)
https-server.js
const https = require('https')
const restana = require('restana')

const service = restana({
  server: https.createServer({
    key: keys.serviceKey,
    cert: keys.certificate
  })
})
service.get('/hi', (req, res) => res.send('Hello World!'))
service.start(3000)
json-api.js
service.get('/api/users', (req, res) => {
  res.send({ users: [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ]})
})
async-handler.js
service.get('/users/:id', async (req, res) => {
  const user = await db.findById(req.params.id)
  if (!user) {
    res.statusCode = 404
    return res.send('Not found')
  }
  res.send(user)
})
nested-routers.js
const nestedRouter = service.newRouter()

nestedRouter.get('/hello', (req, res) => {
  res.send('Hello World!')
})
// Mount under /v1
service.use('/v1', nestedRouter)
middleware.js
// Global logger
service.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`)
  return next()
})

// Route-level auth
service.get('/admin', auth, (req, res) => {
  res.send('Welcome, admin')
})

Core Capabilities

Everything you need, nothing you don't.

1. HTTP Routing

restana supports all standard HTTP methods with a clean, chainable API.

  • GET, DELETE, PUT, PATCH, POST, HEAD, OPTIONS — plus service.all() for universal handlers
  • TRACE is disabled by default (re-enable with enableTrace: true)
  • Route matching uses the 0http trie router by default — router: 'sequential' selects the previous router and its routerCacheSize LRU cache

2. The res.send() Method

One method handles every response type. No res.json(), res.text(), or res.stream() needed.

  • null / undefined — empty response
  • Boolean — application/json
  • String — text/plain
  • Buffer — application/octet-stream
  • Object — application/json
  • Stream — piped with graceful error handling
  • Promise — resolved recursively (depth cap: 3)

Full signature: res.send(data, statusCode, headers, callback). You can also send just a status code: res.send(401).

3. Middleware Support

All middlewares using the (req, res, next) signature are compatible — including Express middleware from npm.

Global Middleware

Applies to every route. Use for logging, CORS, authentication.

Prefix Middleware

Scoped to a path prefix. service.use('/admin', authMiddleware)

Route-Level Middleware

Multiple callbacks per route, including arrays of middleware functions.

Async Middleware (v3.3+)

Full async/await support. Catch uncaught exceptions in the request processing flow with try/catch wrappers.

4. Custom Servers & Events

Server-agnostic. Use standard http.Server, https.Server, or your own implementation.

  • service.start(port) — returns a Promise with the server instance
  • service.close() — graceful shutdown
  • service.callback() — standard (req, res) handler for third-party integrations
  • service.events.BEFORE_ROUTE_REGISTER — hook into route registration

Get Started

One command and you're running.

Configuration Options

Pass an options object to restana(config) — everything is optional.

OptionDescriptionDefault
serverCustom HTTP server instancehttp.createServer()
prioRequestsProcessingUse setImmediate for priority processingtrue
defaultRouteHandler for unmatched routes (404)res.send(404)
errorHandlerGlobal error handler functionSafe by default
routerRouter implementation: 'trie' or 'sequential''trie'
caseSensitiveCase-sensitive path matching (trie router)true
ignoreTrailingSlashTreat /users/ and /users as the same path (trie router)true
routerCacheSizeRoute match cache size (sequential router)2000
enableTraceEnable TRACE HTTP methodfalse
securityHeadersSet default security headers on every responsetrue
trustProxyTrust forwarded protocol for HSTSfalse
debugErrorsExpose local error details (never in production)false
custom-error-handler.js
const service = restana({
  errorHandler(err, req, res) {
    console.error(`Error: ${err.message}`)
    res.send({ error: 'Internal Server Error' }, 500)
  }
})
https-custom-server.js
const https = require('https')
const fs = require('fs')
const service = restana({
  server: https.createServer({
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.pem')
  })
})
service.start(443)
custom-404.js
const service = restana({
  defaultRoute(req, res) {
    res.statusCode = 404
    res.send({ path: req.url, error: 'Not Found' })
  }
})
router-selection.js
const service = restana({
  caseSensitive: false  // trie router, '/Users' matches '/users'
})
// Or: the 6.x sequential router with its LRU cache
// restana({ router: 'sequential', routerCacheSize: 2000 })
enable-trace-debug.js
const service = restana({
  enableTrace: true
})
service.trace('/debug', (req, res) => {
  res.send('Echo: ' + req.url)
})
// ⚠️ Not recommended in production
disable-security-headers.js
const service = restana({
  securityHeaders: false
})
// Use when you manage headers yourself
// (e.g. with Helmet or for non-browser clients)
trusted-reverse-proxy.js
const service = restana({
  trustProxy: true
})
// Enable only when your proxy replaces
// client-supplied forwarded headers.
local-error-debugging.js
const service = restana({
  debugErrors: process.env.NODE_ENV === 'development'
})
// Production always masks error details.

Deploy Anywhere

restana works wherever Node.js runs — from serverless functions to dedicated servers.

☁️ AWS Lambda

Compatible with serverless-http. Wrap your service in a single handler and deploy as a Lambda function.

🔥 Cloud Functions for Firebase

Use service.callback() with functions.https.onRequest() for zero-config Firebase deployment.

📊 Elastic APM

Built-in routes naming plugin for Elastic APM. Instrument your service with restana/libs/elastic-apm.

📈 New Relic APM

Built-in routes naming plugin for New Relic. Attach before registering routes with restana/libs/newrelic-apm.

📄 Swagger / OpenAPI

Validate requests against OpenAPI specs with restana-swagger-validator. Available on npm.

📁 Static Files

Serve static assets with restana-static — a dedicated Docker container for frontend serving.