Skip to content

Repository files navigation

Numflow

High-performance Node.js web framework with Express 5.x compatibility

Numflow is a Node.js web framework that is fully compatible with Express 5.x API while providing 3.3x faster performance on average.

npm versionLicense: MITTests: 1018 passing


Why Numflow?

While most frameworks focus on "how to implement", Numflow focuses on "how to develop and maintain".

As services grow, code becomes complex and business logic gets scattered across multiple files. If you've ever wondered "Where does the logic for this API start and end?", Numflow is the answer.

1. Folder Structure IS the API Specification

Just by looking at features/api/orders/@post, you instantly know it's the POST /api/orders API. No need to hunt for router configurations. The folder name and structure are the URL and HTTP method.

2. Code is the Living Design Document

Keeping design documents and code in sync is nearly impossible. In Numflow, directories and filenames ARE the current implementation and the design. Even after years of maintenance, you can grasp exactly how the system works just by looking at the directory structure—it looks just like a design document. The numbers in filenames are not just labels. Numflow guarantees execution in this numeric order at all times.

  • 100-validate.js
  • 200-check-stock.js
  • 300-payment.js

3. Flexible Structure for Changes

Need to add logic in the middle? No need to rewrite existing code. Just create a 150-check-coupon.js file. Numflow automatically executes it between 100 and 200. Deleting a feature is as simple as deleting a file. Respond quickly to business requirements without worrying about side effects.

4. Perfect Cohesion

All related logic (validation, DB operations, async tasks, etc.) is gathered in one folder. No more wandering through files to modify a feature.


Key Features

Full Express 5.x Compatibility

Numflow provides 100% API compatibility with Express 5.x.

// Expressconstexpress=require('express')constapp=express()// Numflow - just change the require statementconstnumflow=require('numflow')constapp=numflow()

Use existing Express code and middleware without any modifications:

  • express.json() / express.urlencoded()
  • cookie-parser, helmet, morgan, cors
  • passport, multer, express-session
  • All other Express middleware

High Performance

Radix Tree-based routing provides 3.3x (228%) faster performance compared to Express on average.

  • vs Express: +228% (3.3x faster on average)
  • Outperforms Fastify on POST requests (+12%)
  • Feature-First overhead: Only 0.70% (negligible)

For detailed benchmark results, see the Performance Benchmarks section.

Feature-First Architecture

An architecture that automatically executes complex business logic based on folder structure alone, without configuration files.

Zero Configuration: No need to create index.js file. APIs are automatically generated from folders and filenames only.

Folder Structure (Implicit Feature - index.js not required):

features/
api/
orders/
@post/ # POST /api/orders (@ prefix specifies HTTP method)
steps/ # Auto-execute in numeric filename order
100-validate.js # Executes 1st
200-check-stock.js # Executes 2nd
300-create-order.js # Executes 3rd
async-tasks/ # Execute asynchronously after response
send-email.js
send-push.js

index.js is optional. Features are automatically created from folder structure alone.

Folder Structure (Explicit Feature - when additional configuration needed):

features/
api/
orders/
@post/
index.js # ← Additional configuration like contextInitializer, middlewares
steps/
100-validate.js
200-check-stock.js
async-tasks/
send-email.js

When using index.js:

constnumflow=require('numflow')module.exports=numflow.feature({// Additional configuration (optional)contextInitializer: (ctx,req,res)=>{ctx.startTime=Date.now()},middlewares: [authMiddleware,rateLimitMiddleware],})

Application Registration:

constnumflow=require('numflow')constapp=numflow()app.registerFeatures('./features')// Auto-scan folder structureapp.listen(3000)

Core Features:

  1. Zero Configuration

    • No need to create index.js file
    • APIs automatically generated from folder structure alone
    • HTTP method, path, steps, async-tasks all auto-inferred
    • Use index.js optionally only when additional configuration needed
  2. Auto-execution Order Based on Numeric Filenames

    • Automatically execute in order: 100-, 200-, 300-
    • Execution order clearly expressed in filenames
    • No separate configuration file needed
  3. Understand Business Logic from Folder Structure

    • Understand entire flow without opening code
    • features/api/orders/@post/steps/ → Processing steps for POST /api/orders
    • @ prefix explicitly expresses HTTP method (prevents collision with resource names)
    • Each filename describes the role of that step
  4. Flexible Logic Management

    • Add: Create 150-check-user-auth.js and it automatically inserts and executes between 100 and 200
    • Delete: Remove step by deleting file
    • Reorder: Change execution order by changing filename numbers
    • Change logic structure without modifying existing code

Why use @ prefix:

The @ prefix explicitly distinguishes HTTP method folders to prevent collision with resource names.

# Collision occurs without @ prefix
features/workflows/[id]/steps/get/ # ← Is "steps" a resource or a folder?
# Clearly distinguished with @ prefix
features/workflows/[id]/steps/@get/ # ← GET /workflows/:id/steps
└─ resource name
└─ HTTP method

Additional Folder Structure Examples:

features/
users/@get/ # GET /users
users/@post/ # POST /users
users/[id]/@get/ # GET /users/:id
users/[id]/@put/ # PUT /users/:id
users/[id]/@delete/ # DELETE /users/:id
api/v1/orders/@post/ # POST /api/v1/orders

Additional Features:

  • Transaction Management: Transaction management via contextInitializer, onError hooks
  • Auto-execute Async Tasks: Automatic async-tasks execution after response
  • Centralized Error Handling: Unified error handling with onError hook
  • Minimal Overhead: 0.70% performance overhead (based on 10 steps)

WebSocket Support

Numflow supports WebSocket with 100% Express compatibility.

constnumflow=require('numflow')const{ WebSocketServer }=require('ws')constapp=numflow()constserver=app.listen(3000)// ws libraryconstwss=newWebSocketServer({noServer: true})server.on('upgrade',(request,socket,head)=>{wss.handleUpgrade(request,socket,head,(ws)=>{wss.emit('connection',ws,request)})})// Socket.IO fully supportedconstio=require('socket.io')(server)io.on('connection',(socket)=>{socket.emit('welcome',{message: 'Connected!'})})

Full ESM and CommonJS Support

Numflow perfectly supports all module systems.

// CommonJSconstnumflow=require('numflow')// ESMimportnumflowfrom'numflow'// TypeScriptimportnumflowfrom'numflow'importtype{Application,Request,Response}from'numflow'

All file extensions supported:

  • .js, .cjs (CommonJS)
  • .mjs, .mts (ESM)
  • .ts (TypeScript)

Installation

npm install numflow

Quick Start

Hello World (JavaScript)

CommonJS:

constnumflow=require('numflow')constapp=numflow()app.get('/',(req,res)=>{res.json({message: 'Hello Numflow'})})app.listen(3000)

ESM:

importnumflowfrom'numflow'constapp=numflow()app.get('/',(req,res)=>{res.json({message: 'Hello Numflow'})})app.listen(3000)

TypeScript

importnumflowfrom'numflow'constapp=numflow()app.get('/',(req,res)=>{res.json({message: 'Hello Numflow'})})app.listen(3000)

Migration from Express

Step 1: Install Package

npm install numflow

Step 2: Change Import Statement

// Beforeconstexpress=require('express')// Afterconstnumflow=require('numflow')

Step 3: Done

The rest of your code works without modification.

Compatible items:

  • All HTTP methods (GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD)
  • All middleware
  • req.params, req.query, req.body
  • res.json(), res.send(), res.status(), res.redirect()
  • Router, app.use(), app.all()
  • express.static()

Verification status:

  • 1,018 tests passing 100%
  • Express 5.x API compatibility verified
  • Major middleware compatibility verified

Feature-First Usage Examples

Order Creation API

Simplest Method (index.js not required):

features/
api/
orders/
@post/
steps/
100-validate-request.js
200-check-user-auth.js
300-check-product-stock.js
400-create-order.js
500-process-payment.js
async-tasks/
send-order-email.js
send-push-notification.js
update-analytics.js

The POST /api/orders API is automatically created from folder structure alone.

When Additional Configuration Needed (using index.js):

features/
api/
orders/
@post/
index.js # ← Additional configuration (contextInitializer, middlewares, onError Hook)
steps/
...

features/api/orders/@post/index.js:

constnumflow=require('numflow')module.exports=numflow.feature({// method, path, steps, asyncTasks are auto-inferred from folder structure// Add configuration only when neededcontextInitializer: (ctx,req,res)=>{ctx.startTime=Date.now()ctx.userId=req.user?.id},})

steps/100-validate-request.js:

module.exports=async(ctx,req,res)=>{const{ productId, quantity }=req.bodyif(!productId||!quantity){thrownewError('productId and quantity are required')}ctx.productId=productIdctx.quantity=quantity}

steps/300-check-product-stock.js:

module.exports=async(ctx,req,res)=>{constproduct=awaitdb.products.findById(ctx.productId)if(product.stock<ctx.quantity){thrownewError('Insufficient stock')}ctx.product=product}

steps/400-create-order.js:

module.exports=async(ctx,req,res)=>{constorder=awaitdb.orders.create({userId: req.user.id,productId: ctx.productId,quantity: ctx.quantity,totalPrice: ctx.product.price*ctx.quantity,})ctx.order=orderres.json({success: true,orderId: order.id})}

async-tasks/send-order-email.js:

module.exports=async(ctx)=>{awaitemailService.send({to: ctx.order.userEmail,template: 'order-confirmation',data: {order: ctx.order},})}

app.js:

constnumflow=require('numflow')constapp=numflow()app.registerFeatures('./features')app.listen(3000)

Flexible Logic Management Example

Scenario: Adding Authorization Check Logic

When you need to add a new step between existing logic:

# Existing structure
steps/
100-validate-request.js
300-check-product-stock.js
400-create-order.js
# Insert logic by adding new file only
steps/
100-validate-request.js
200-check-user-auth.js # ← Newly added (no modification to existing code)
300-check-product-stock.js
400-create-order.js

Create steps/200-check-user-auth.js:

module.exports=async(ctx,req,res)=>{if(!req.user||!req.user.isActive){thrownewError('Unauthorized')}ctx.userId=req.user.id}

Just restart server and it automatically executes in order: 100 → 200 → 300 → 400.

Delete Logic:

# Remove stock check step
rm steps/300-check-product-stock.js
# Restart server → automatically executes in order: 100 → 200 → 400

Change Execution Order:

# When you want to execute payment before order creation
mv steps/500-process-payment.js steps/350-process-payment.js
mv steps/400-create-order.js steps/450-create-order.js
# Restart server → executes in order: 100 → 200 → 300 → 350(payment) → 450(order)

Performance Benchmarks

Test Environment

  • Node.js v22.11.0
  • macOS (Apple Silicon)
  • Autocannon (100 connections, 10s duration)

Overall Results

ScenarioExpressNumflowFastifyvs Expressvs Fastify
Hello World20,542 req/s75,626 req/s89,108 req/s+268%-15%
JSON Response (GET)20,421 req/s65,574 req/s86,607 req/s+221%-24%
JSON Parse (POST)18,151 req/s57,872 req/s51,664 req/s+219%+12% ⭐
Route Params (single)19,790 req/s65,734 req/s84,025 req/s+232%-22%
Route Params (multiple)19,982 req/s62,387 req/s80,992 req/s+212%-23%
Route + Query19,893 req/s61,988 req/s85,082 req/s+212%-27%
Middleware Chain19,080 req/s63,254 req/s83,837 req/s+232%-25%
Average19,694 req/s64,634 req/s80,188 req/s+228%-19%

Feature-First Performance

The performance overhead of Feature-First architecture is only 0.70%, which is negligible:

  • Regular Route: 49,714 req/s
  • Feature (10 Steps): 49,366 req/s
  • Overhead: 0.70% (based on 10 steps)

You can structure complex business logic with almost no performance loss.

For detailed benchmark results, see PERFORMANCE.md


Testing and Verification

Tests

  • 1,018 tests passing 100%
  • Core functionality tests
  • Express compatibility tests
  • Middleware compatibility tests
  • Feature-First integration tests
  • Performance regression tests

Express Compatibility

  • Express 5.x API 100% compatible
  • Major Express middleware verified
  • Validated with 1,018 tests

Detailed compatibility info: COMPATIBILITY.md


Documentation

Getting Started

Advanced

Performance

Compatibility


Showcase

Real-World Examples

Check out real-world projects built with Numflow's Feature-First architecture:

A fully functional blog application built with Feature-First architecture. It implements the following features:

  • Post CRUD (Create, Read, Update, Delete)
  • Comment system
  • User authentication and authorization
  • File upload (images)
  • Pagination
  • Search functionality

What you can learn from this example:

  • How to design Feature-First folder structure
  • How to structure business logic with Steps and Async Tasks
  • Data sharing using Context
  • Transaction management and error handling patterns

Contributing

Issue reports, feature suggestions, documentation improvements, and code contributions are welcome.


FAQ

Q: Is it 100% compatible with Express?

A: Verified with 1,018 tests. All core APIs and major middleware of Express 5.x are compatible.

Q: Is Feature-First mandatory?

A: It's optional. You can use it with Express-style only.

Q: Is TypeScript required?

A: No. JavaScript (CommonJS/ESM) is fully supported. TypeScript is optional.

Q: Is the 3.3x faster performance the same in production environments?

A: Benchmark results show 3.3x (228%) on average. Actual performance may vary depending on application structure, middleware usage, and business logic.

Q: What's the difference from Fastify?

A: Fastify uses its own API. Numflow maintains Express API while providing performance close to Fastify.


License

MIT License


Acknowledgements

Special thanks to my wife Carol for her unwavering support and encouragement throughout this project.

I am also grateful to my family for their patience and understanding during the development of this framework.


References

This project was inspired by ideas from the following projects:

Releases

Packages

Contributors

Languages