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.
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.
const restana = require('restana')
const service = restana()
service.get('/hi', (req, res) => res.send('Hello World!'))
service.start(3000)
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)
service.get('/api/users', (req, res) => {
res.send({ users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]})
})
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)
})
const nestedRouter = service.newRouter()
nestedRouter.get('/hello', (req, res) => {
res.send('Hello World!')
})
// Mount under /v1
service.use('/v1', nestedRouter)
// 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 itsrouterCacheSizeLRU cache
2. The res.send() Method
One method handles every response type. No res.json(), res.text(), or res.stream() needed.
null/undefined— empty responseBoolean— application/jsonString— text/plainBuffer— application/octet-streamObject— application/jsonStream— piped with graceful error handlingPromise— 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 instanceservice.close()— graceful shutdownservice.callback()— standard(req, res)handler for third-party integrationsservice.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.
| Option | Description | Default |
|---|---|---|
| server | Custom HTTP server instance | http.createServer() |
| prioRequestsProcessing | Use setImmediate for priority processing | true |
| defaultRoute | Handler for unmatched routes (404) | res.send(404) |
| errorHandler | Global error handler function | Safe by default |
| router | Router implementation: 'trie' or 'sequential' | 'trie' |
| caseSensitive | Case-sensitive path matching (trie router) | true |
| ignoreTrailingSlash | Treat /users/ and /users as the same path (trie router) | true |
| routerCacheSize | Route match cache size (sequential router) | 2000 |
| enableTrace | Enable TRACE HTTP method | false |
| securityHeaders | Set default security headers on every response | true |
| trustProxy | Trust forwarded protocol for HSTS | false |
| debugErrors | Expose local error details (never in production) | false |
const service = restana({
errorHandler(err, req, res) {
console.error(`Error: ${err.message}`)
res.send({ error: 'Internal Server Error' }, 500)
}
})
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)
const service = restana({
defaultRoute(req, res) {
res.statusCode = 404
res.send({ path: req.url, error: 'Not Found' })
}
})
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 })
const service = restana({
enableTrace: true
})
service.trace('/debug', (req, res) => {
res.send('Echo: ' + req.url)
})
// ⚠️ Not recommended in production
const service = restana({
securityHeaders: false
})
// Use when you manage headers yourself
// (e.g. with Helmet or for non-browser clients)
const service = restana({
trustProxy: true
})
// Enable only when your proxy replaces
// client-supplied forwarded headers.
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.