@furi-server/furi
v0.14.2
Published
Furi - The Fast HTTP/S Node.js Server Framework
Maintainers
Readme
Furi - Fast HTTP/HTTPS Server framework
Benchmarks 🚀
Furi outperforms both Fastify and Express.js 5.0 in a benchmark test. Below are the benchmarks results.
- Number of requests made: 100,000
- Total time taken in seconds.
- Requests handled in 1 second.
Framework | Requests | Total Time | Requests/Seconds| Built with TypeScript -|-|-|-|- Furi | 100,000 | 11.569 s | 8643.74| ✅ Fastify | 100,000 | 13.847 s | 7221.62| ❌ Express.js v5.0 | 100,000 | 18.020 s | 5549.29| ❌
FEATURE UPDATES 🚨

- New embedded Sqlite3 support for in-memory application state management.
- Minor breaking-change with logger configuration and code refactoring.
Planned work for Sqlite3 and state management. 🎯
- Allow state management to persiste on disk.
- Add configuration to choose betweek in-memory and disk-based state management.
- Add seamless caching support to speed response time for web pages and REST APIs.
- Add an application cache store, for custom caching logic and data retrieval.
- Add schema support to declare the structure of the application store database table.
- Add new accessor methods to allow for flexible types when using the state management system.
Always check the changelog before pulling updates to see what changed. Furi is in early preview mode, so expect rapid changes, some breaking.
- Furi - Fast HTTP/HTTPS Server framework
- Benchmarks 🚀
- FEATURE UPDATES 🚨
- A Return to Simplicity ✅
- BOM - Bill of Material
- Example source code
- Coding with JavaScript
- Startup message
- Declare a named route
- Supported middlewares
- Array based routing
- Server Configuration file
- Configuration file
- Server properties
- Super fast stream logging ⚡
- Enabling HTTPS Support
- Motivation
- Why
- Benchmarks 🚀
A Return to Simplicity ✅
Furi is a Node.js framework coded in TypeScript. If you love TypeScript, you will feel at home coding with Furi. If you love plain JavaScript, you will love coding in Furi, you get to decide.
The design has been kept as close to the Node.js API without using external dependencies. Coded using modern JavaScript and the latest Node.js APIs.
The Router was coded from the ground up in TypeScript, it is the core of the framework, with a blazing fast lookup and matching algorithm.
Zero useless abstraction, simple clean coding, designed for hardware with small resources. Perfect for micro-architecture. Very little between your code and the Node.js API to minimize performance overhead.
Router has been battle tested with unit tests and functional tests.
A self contained design and zero external dependencies means there is less surface area for bugs and security issues to hide and creep in. There is less likelihood for working code to break after pulling in updates. Having to maintain perfect working code broken due to an update is an anti-pattern and an insane mindset to develop software.
Furi will keep simple things simple and make hard things easier without breaking your working code. It is however still in the early preview stage so expect changes as I explore design ideas.
Furi is currently under active development. However it is feature complete with respect to the Router, and today could be put into production use. Current development effort is focused on adding support for a easy to use State management store for seamless data access. Persistence using SQLite3 as the default "embedded" database engine, with a plug-in architecture for other DB engines.
BOM - Bill of Material
The following tools, technologies and software was used in the development of Furi (v0.1.4).
Item | Version | Description --- | --- | --- TypeScript | 5.8.3 | A superset of JavaScript that adds static typing and other features to the language. node | 22 LTS | Node.js core APIs. yaml | 2.7.1 | A library for parsing YAML used to read FURI configuration file. better-sqlite3 | 7.6.13 | A library for interacting with SQLite databases. Linux | 6.13.1-1-default | openSUSE Tumbleweed with KDE desktop
NOTE: See Changelog for additional details on changes and updates. ✅
Example source code
You can find example source code at the GitHib furi-examples repository.
You can download the example source use using git:
git clone https://github.com/rajinder-yadav/furi-examples.gitThe examples are easy to follow and should give you to a good understanding of how to use the Furi framework.
For more the Typescript examples and can install and use Deno. The first example in directory, "01-simple-js-node" shows you how to use Node.js with plain JavaScript. The examples are number to help you quick start from basic and move to advanced usage.
Coding with JavaScript
File: "main.js"
import { Furi } from '@furi-server/furi';
const furi = Furi.create();
furi.get('/', (ctx) => {
return { message: 'Hello World' };
});
furi.start();Coding with TypeScript
You can use TypeScript with Node.js, but you will need to compile the TypeScript code to JavaScript before running it with Node.js.
With Deno it is simpler, as it will run the TypeScript code without needing a separate compile step.
File: "main.ts"
import { Furi, ApplicationContext } from '@furi-server/furi';
const furi = Furi.create();
furi.get('/', (ctx: ApplicationContext) => {
return { message: 'Hello World' };
});
furi.start();Using NPM and Node.js
To install the NPM package, use:
npm install @furi-server/furiUsing Deno
If you are using Deno, add the package with:
deno add npm:@furi-server/furiStartup message
When you run the server application, you will see a similar output in your terminal:
INFO, Furi::listen Creating a unsecure HTTP server.
INFO, Furi Server (v0.14.0) started.
INFO, Server { mode: http, host: localhost, port: 3030, env: development }
INFO, Runtime { node: 22.14.0, v8: 12.4.254.21-node.22 }
INFO, Logger { enable: true, level: INFO, logFile: ./logs/furi.log, mode: stream, flushPeriod: 1000ms, maxCount: 100, rollover: 24h }
This can help you quickly identify that your server is running, configuration settings and the runtime environment details.
Declare a named route
The code below shows how to declare a named route, and also how to read the named route parameters from the handler function, using the ApplicationContext object.
furi.get("/about/:user_id", (ctx: ApplicationContext) => {
ctx.response.writeHead(200, {
"Content-Type": "text/html",
"User-Agent": USER_AGENT
});
ctx.end(`<p>User page for: ${ctx.request.params.user_id}</p>\n`);
});Use a router to declare routes
Below we declare a route handler on a router, then we mouth the router to the Furi instance.
const furi = Furi.create();
const router = Furi.router();
router.get('/home', (ctx: ApplicationContext) => {
ctx.response.writeHead(200, {
'Content-Type': 'text/html',
'User-Agent': USER_AGENT
});
ctx.send('<h1>Home Page</h1>\n');
ctx.send('<p>Welcome to the home page.</p>\n');
ctx.end();
});
furi.use(router);Mounting to a route path:
This will mount the router to the "/v1/api" path. The "/home" route will be accessible at "/v1/api/home".
furi.use('/v1/api', router);Mounting top-level middlewares
You can mount top-level middlewares to the Furi instance. These middlewares will be executed for every request.
- A top-level middleware is mounted using "use()" method.
- The handler function take two arguments:
- Application context object.
- Next function to call the next middleware or handler.
NOTE: The last handler function must end the request with a call to "end()", or returning a value.
router.use((ctx: ApplicationContext, next: Middleware) => {
ctx.send('Top-level Middleware 1\n');
next();
});
router.use((ctx: ApplicationContext, next: Middleware) => {
ctx.send('Top-level Middleware 2\n');
next();
});Declaring route based middlewares
router.get('/home', (ctx: ApplicationContext, next: Middleware) => {
ctx.send('Middleware 1\n');
next();
});
router.get('/home', (ctx: ApplicationContext, next: Middleware) => {
ctx.send('Middleware 2\n');
next();
});
router.get('/home', (ctx: ApplicationContext, next: Middleware) => {
ctx.send('<h1>Home Page</h1>\n');
ctx.end('<p>Welcome to the home page.</p>\n');
});Supported middlewares
Furi currently support the following core middlewares:
- Body parser
- Cors
- Web
Documentation forth coming once I have time to write it.
Array based routing
Furi now supports array based routes. You declare one or more routes in the "routes" array.
Each route entry requires three properties:
- method
- path
- controller
NOTE: the "controller" property can be a single handler function, or multiple handlers declared inside the "controller" array. See: Declaring route-level middleware.
Here is an example of a route with an inline lambda handler:
import { Furi } from '@furi-server/furi';
const furi = Furi.create();
const routes: Routes = {
routes: [
{
method: 'get',
path: '/one',
controller: (ctx: ApplicationContext, next: Middleware) => {
ctx.response.writeHead(200, {
'Content-Type': 'text/html',
'User-Agent': USER_AGENT
});
ctx.end('Middleware Pre!\n');
}
}
]
}
furi.use(routes);You can also mount the array route on a path:
furi.use('/v1/api', routes);You can also mount the array on a router and then mount that to the app:
const router = Furi.router();
router.use(routes);
furi.use('/admin',router);NOTE: Top-level middlewares, even when mounted to a router, that are then mounted to the route-path will always remain top-level middlewares. Array based middleware routes are declared further below.
Declaring a Handler Class
To declare the class based handler, you will need to:
- Subclass "BaseRouterHandler".
- Override the "handle()" method.
NOTE: You can also declare a class based middleware, the handler function will also need to accept the "next" argument.
class HelloWordHandler extends BaseRouterHandler {
override handle(ctx: ApplicationContext): any {
ctx.response.writeHead(200, {
'Content-Type': 'text/plain',
'User-Agent': USER_AGENT
});
// ctx.end('HelloWordHandler\n');
return 'HelloWordHandler\n';
}
}In the router array, you simply pass the class name to the controller property:
const routes: Routes = {
routes: [
{
method: 'get',
path: '/helloworld',
controller: HelloWordMiddlewareHandler
}
]
};
const router = Furi.router();
router.use(routes);Declaring top-level middleware
Remember will middleware, from the handler function you will need to call "next()" to pass control to the next middleware or handler.
function myMiddleware(ctx: ApplicationContext, next: Middleware) {
ctx.response.writeHead(200, {
'Content-Type': 'text/html',
'User-Agent': USER_AGENT
});
ctx.send('Middleware Pre!\n');
next();
}In the router array, the top-level middlewares are declared in the middleware array:
const routes: Routes = {
middleware: [
myMiddleware
],
routes: [
...
]
};Declaring route-level middleware
As with the function based routes, you can also declare route-level middleware in route array:
const routes: Routes = {
routes: [
{
method: 'get',
path: '/one',
controller: (ctx: ApplicationContext, next: Middleware) => {
ctx.response.writeHead(200, {
'Content-Type': 'text/html',
'User-Agent': USER_AGENT
});
ctx.send('Middleware Pre!\n');
next();
}
},
{
method: 'get',
path: '/one',
controller: (ctx: ApplicationContext, next: Middleware) => {
ctx.response.writeHead(200, {
'Content-Type': 'text/html',
'User-Agent': USER_AGENT
});
ctx.end('Hello World!\n');
}
},
]
};Since you are declaring multiple route handlers on the same route, you can simplify the declaration. Just combine the handler functions in the "controller" array, like this:
const routes: Routes = {
routes: [
{
method: 'get',
path: '/one',
controller: [
(ctx: ApplicationContext, next: Middleware) => {
ctx.response.writeHead(200, {
'Content-Type': 'text/html',
'User-Agent': USER_AGENT
});
ctx.send('Middleware Pre!\n');
next();
},
(ctx: ApplicationContext, next: Middleware) => {
ctx.response.writeHead(200, {
'Content-Type': 'text/html',
'User-Agent': USER_AGENT
});
ctx.end('Hello World!\n');
}
]
},
]
};Server Configuration file
Furi lets you configure server settings from a YAML file. This allows you to easily change settings without having to modify your code.
Currently the configurable setting can control. All these settings are optional.
- Server start up properties.
- Logging properties.
- HTTPS properties.
Configuration file
The server configuration must be called, "furi.yaml" or "furi.yml" and placed under the project root directory.
If you configuration file is found, Furi will use sensible defaults.
File: "furi.yaml" (optional)
Server properties
This configuration control the server startup properties.
server:
port: 3030
host: localhost
env: developmentSuper fast stream logging ⚡
Furi supports fist-class logging at the core. Logging is fast and takes place on a background worker-thread, so the main thread never blocks. Logging can be buffered, or immediately written to file. Logging behavior can be configured in Furi's configuration YAML file.
Logging uses the latest Node.js features. Since logging is the core functionality of Furi, there is very little code overhead compared to existing logging libraries.
Note file logging is disabled by default, you must enable it in Furi YAML configuration file.
Logger configuration
Here are the configurable logging options:
- enable: Turn logging on or off.
- flushPeriod: Control time to flush buffered log messages.
- maxCount: Maximum number of log messages before flushing.
- mode: Can be one of "stream" or "buffer".
- level: Can be one of "debug", "info", "log", "warn", "error", "critical" or "fatal".
- logDir: Log directory, will be created if it does not exist.
- logFile: Log filename.
- rollover: Maximum number of days before log file is rolled over.
The level is used to filter log messages based on their severity. Only messages at or above the configured level will be logged.
If you do not declare any logger settings, the following are the default setting values:
logger:
enable: false
flushPeriod: 1000
logDir: logs
logFile: furi.log
maxCount: 100
mode: buffer
level: info
rollover: 24To enable logging you only need to change one setting:
logger:
enable: trueThis will result in buffered logging, if you want to view immediate logging, you can switch to stream mode:
logger:
enable: true
mode: streamLog levels
It is suggested for Users application code, you log at the "log" level. The framework logs at the "info" level, to provide additional information on the request. However should you ever want to limit logging to your own application code while developing, it will help reduce the log noise.
The following log levels are supported, list in increasing order of severity:
Level | Description ------|------------- debug | Verbose output for debugging purposes. info | Default log, details operations information. log | General User application level logging. warn | State that is not a normal operation. error | Application level error needing investigation. critical | System level error that may cause application to fail. fatal | Unrecoverable error causing application to terminate.
Enabling HTTPS Support
Furi makes it easy to spin-up a HTTPS server. You do not need to code this up manually in your server application code.
Furi is started with HTTPS support by providing the path to the SSL key and certificate files in the"furi.yaml" server configuration file.
You must declare the properties under the "cert" section as follows:
Using a SSL certificate
Here is how you would start Furi with HTTPS:
cert:
key: ./ssl/key.pem
cert: ./ssl/cert.pemUsing a SSL certificate with a passphrase
cert:
key: ./ssl/key.pem
cert: ./ssl/cert.pem
passphrase: hello123When Furi is running under HTTPS, it will shows in the log and startup message. You will see "__mode: https" under Server.
Furi Server (v0.14.0) started.
Server { mode: http, host: localhost, port: 3030, env: development }
Runtime { deno: 2.2.5, v8: 13.5.212.4-rusty, typescript: 5.7.3 }
Logger { enable: true, level: INFO, logFile: ./logs/furi.log, mode: stream, flushPeriod: 1000ms, maxCount: 100, rollover: 24h }Sample log output
2025-03-23T04:11:05.018Z, INFO, Furi::listen Creating a unsecure HTTP server.
2025-03-23T04:11:05.019Z, INFO, Furi Server (v0.13.3) started.
2025-03-23T04:11:05.019Z, INFO, Server { mode: http, host: localhost, port: 3030, env: development }
2025-03-23T04:11:05.019Z, INFO, Runtime { deno: 2.2.5, v8: 13.5.212.4-rusty, typescript: 5.7.3 }
2025-03-23T04:11:05.020Z, INFO, Logger { enable: true, level: INFO, logFile: furi.log, mode: stream, flushPeriod: 1000ms, maxCount: 100 }
2025-03-23T04:11:09.343Z, INFO, host: localhost:3030, remote-ip: 127.0.0.1, remote-port: 60452, http: 1.1, method: GET, url: /
2025-03-23T04:11:09.363Z, INFO, host: localhost:3030, remote-ip: 127.0.0.1, remote-port: 60466, http: 1.1, method: GET, url: /
2025-03-23T04:11:09.368Z, INFO, host: localhost:3030, remote-ip: 127.0.0.1, remote-port: 60478, http: 1.1, method: GET, url: /about
2025-03-23T04:11:09.373Z, INFO, host: localhost:3030, remote-ip: 127.0.0.1, remote-port: 60488, http: 1.1, method: GET, url: /about/
2025-03-23T04:11:09.377Z, INFO, host: localhost:3030, remote-ip: 127.0.0.1, remote-port: 60504, http: 1.1, method: GET, url: /about/raj12Motivation

The primary objective of the Furi project is to provide a fast, small HTTP server that runs on small hardware with low memory. This benefits micro-architect environments with scaling and performance, with faster load time, compact footprint to maximize bigger production workloads.
The guiding principle of the project is to have the code base self contain with no external dependencies. This allows for easy deployment and maintenance on any platform that supports Node.js. The aim is for small independent shops to be able to run a production server and website while keeping the cost down substantially, along with the effort to maintain the setup.
Why
A fast, responsive and lightweight framework that is easy to use. Furi keeps your code simple, avoids useless abstraction and does not get in the way with working with Node.js core APIs should you ever need to.
Inspired by Express.js and Koa.
Benchmarks 🚀
Furi outperformed both Fastify and Express.js 5.0 in a benchmark test. Below are the benchmarks results.
- Number of requests made: 100,000
- Total time taken in seconds.
- Requests handled in 1 second.
Framework | Requests | Total Time | Requests/Seconds| Built with TypeScript -|-|-|-|- Furi | 100,000 | 11.569 s | 8643.74| ✅ Fastify | 100,000 | 13.847 s | 7221.62| ❌ Express.js v5.0 | 100,000 | 18.020 s | 5549.29| ❌
Furi Benchmark

Fastify Benchmark

Express Benchmark

