Skip to content

ncwardell/NanoWarp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

43 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

NanoWarp

Lightning-Fast API Framework with Hot-Swappable File-Based Endpoints

NPM Version License: MIT TypeScript Bun Node.js

Quick Start β€’ Documentation β€’ Examples β€’ Contributing

πŸš€ Overview

NanoWarp is a modern, high-performance API framework that eliminates the complexity of traditional backend development. Built with cross-runtime compatibility for both Bun and Node.js, it offers instant hot-reload, file-based routing, and production-grade reliabilityβ€”all in a lightweight, dependency-minimal package.

Core Philosophy

Drop a .ts file in a folder β†’ instant API endpoint. Edit it β†’ changes apply immediately. No restart. No rebuild. No configuration.

// data/Endpoints/GET/hello.ts
export const execute = async (path, request, Database) => {
    return new Response('Hello World!');
};

Access your endpoint: http://localhost:3000/hello ✨


✨ Key Features

Developer Experience

  • ⚑ Hot Reload - File watcher detects changes and reloads endpoints instantly
  • πŸ“ File-Based Routing - File location maps directly to API paths
  • 🎯 Zero Configuration - No config files, no build steps, just code
  • πŸ“¦ Minimal Dependencies - Lightweight footprint for fast installs and deploys
  • πŸ”„ Cross-Runtime - Works seamlessly on both Bun and Node.js

Production Ready

  • πŸ›‘οΈ Error Boundaries - Endpoint crashes don't kill the server
  • ⏱️ Request Timeouts - 30-second automatic timeout prevents infinite loops
  • πŸ” API Key Authentication - Built-in auth with expiration and path whitelisting
  • πŸ“Š Rate Limiting - Token bucket algorithm prevents abuse
  • πŸ”’ Atomic Operations - File-level locks prevent data corruption
  • 🧹 Graceful Shutdown - Waits for in-flight requests before stopping

Data Management

  • πŸ’Ύ File-Based Database - Your filesystem IS the database
  • βš›οΈ Atomic Writes - Temp file + atomic rename ensures data integrity
  • πŸ” Mutex Locks - Per-file write queues prevent concurrent write issues
  • πŸ“‚ Directory Indexing - Fast lookups with cached directory structure

πŸ“¦ Installation

# Using npm
npm install nanowarp

# Using Bun
bun add nanowarp

# Using pnpm
pnpm add nanowarp

# Using yarn
yarn add nanowarp

Requirements:

  • Node.js 18+ or Bun 1.0+
  • TypeScript 5.0+ (recommended)

πŸš€ Quick Start

1. Create Your Server

import { NanoWarp } from 'nanowarp';

// Default configuration (port 3000, ./data directory)
const server = new NanoWarp();
await server.start();

console.log('πŸš€ Server running on http://localhost:3000');

Run with Bun (recommended):

bun server.ts

Run with Node.js:

npx tsx server.ts
# or after building: node dist/server.js

2. Create Your First Endpoint

mkdir -p data/Endpoints/GET

Create data/Endpoints/GET/hello.ts:

export const execute = async (path: string, request: Request, Database: any) => {
    return new Response('Hello from NanoWarp! πŸ‘‹', {
        status: 200,
        headers: { 'Content-Type': 'text/plain' },
    });
};

3. Test Your Endpoint

curl http://localhost:3000/hello
# Output: Hello from NanoWarp! πŸ‘‹

Custom Configuration

import { NanoWarp } from 'nanowarp';

// Custom port and data directory
const server = new NanoWarp(8080, './my-data');
await server.start();

πŸ“– Documentation

File-Based Routing

File paths automatically map to URL endpoints based on HTTP method and location:

data/Endpoints/
β”œβ”€β”€ GET/
β”‚   β”œβ”€β”€ users.ts              β†’ GET /users
β”‚   β”œβ”€β”€ users/
β”‚   β”‚   └── profile.ts        β†’ GET /users/profile
β”‚   └── health.ts             β†’ GET /health
└── POST/
    β”œβ”€β”€ users.ts              β†’ POST /users
    └── auth/
        └── login.ts          β†’ POST /auth/login

Endpoint Structure

Every endpoint must export an execute function. Optionally, you can export rateLimit and schema configurations:

// Required: Execute function
export const execute = async (
    path: string,        // The request path (e.g., "users/profile")
    request: Request,    // Web Standards Request object
    Database: DataManager // NanoWarp database manager instance
): Promise<Response> => {
    // Your endpoint logic here
    return new Response('Success', {
        status: 200,
        headers: { 'Content-Type': 'text/plain' }
    });
};

// Optional: Rate limiting configuration (disabled by default)
export const rateLimit = {
    enabled: true,
    maxTokens: 100,
    refillRate: 10,
    refillInterval: 1000,
};

// Optional: OpenAPI schema (auto-generated defaults if not provided)
export const schema = {
    summary: 'Endpoint description',
    description: 'Detailed endpoint documentation',
    tags: ['API'],
    // ... other OpenAPI schema properties
};

Working with Data

NanoWarp provides a simple yet powerful file-based data API:

// Read data
const data = await Database.retrieveData('./data/users.json');

// Write data (atomic + locked for safety)
await Database.saveData('./data/users.json', JSON.stringify(data));

// Delete data
await Database.deleteData('./data/users.json');

Smart Data Handling:

  • .json and .lock files β†’ Parsed JSON object
  • Directories β†’ Array of filenames
  • Other files β†’ ArrayBuffer (binary data)

πŸ’‘ Examples

Complete REST API: Todo Application

GET Endpoint - List Todos

data/Endpoints/GET/todos.ts:

// Only the execute function is required
export const execute = async (path, request, Database) => {
    try {
        const todos = await Database.retrieveData('./data/todos.json') || [];

        return new Response(JSON.stringify(todos), {
            status: 200,
            headers: { 'Content-Type': 'application/json' }
        });
    } catch (error) {
        return new Response(JSON.stringify({ error: 'Failed to retrieve todos' }), {
            status: 500,
            headers: { 'Content-Type': 'application/json' }
        });
    }
};

POST Endpoint - Create Todo

data/Endpoints/POST/todos.ts:

export const execute = async (path, request, Database) => {
    try {
        const body = await request.json();

        // Validate input
        if (!body.title) {
            return new Response(JSON.stringify({ error: 'Title is required' }), {
                status: 400,
                headers: { 'Content-Type': 'application/json' }
            });
        }

        // Retrieve existing todos
        const todos = await Database.retrieveData('./data/todos.json') || [];

        // Create new todo
        const newTodo = {
            id: Date.now(),
            title: body.title,
            completed: false,
            createdAt: new Date().toISOString()
        };

        todos.push(newTodo);

        // Save atomically
        await Database.saveData('./data/todos.json', JSON.stringify(todos, null, 2));

        return new Response(JSON.stringify(newTodo), {
            status: 201,
            headers: { 'Content-Type': 'application/json' }
        });
    } catch (error) {
        return new Response(JSON.stringify({ error: 'Failed to create todo' }), {
            status: 500,
            headers: { 'Content-Type': 'application/json' }
        });
    }
};

Test Your API:

# Create a todo
curl -X POST http://localhost:3000/todos \
  -H "Content-Type: application/json" \
  -d '{"title":"Build awesome API"}'

# List all todos
curl http://localhost:3000/todos

Query Parameters and URL Parsing

data/Endpoints/GET/users/search.ts:

export const execute = async (path, request, Database) => {
    const url = new URL(request.url);
    const name = url.searchParams.get('name');
    const limit = parseInt(url.searchParams.get('limit') || '10');

    // Fetch and filter users
    const allUsers = await Database.retrieveData('./data/users.json') || [];
    const filtered = name
        ? allUsers.filter(u => u.name.includes(name))
        : allUsers;

    return new Response(JSON.stringify(filtered.slice(0, limit)), {
        headers: { 'Content-Type': 'application/json' }
    });
};
# Test with query parameters
curl "http://localhost:3000/users/search?name=John&limit=5"

πŸ” Security

API Key Authentication

Create data/apikeys.json:

{
  "keys": {
    "prod-key-abc123": "2025-12-31T23:59:59.000Z",
    "dev-key-xyz789": "2024-06-30T23:59:59.000Z"
  },
  "whitelist": ["/health", "/public"]
}

Features:

  • ⏱️ Automatic Expiration - Keys expire based on ISO date
  • 🎯 Path Whitelisting - Public endpoints bypass authentication
  • πŸ’Ύ 60-Second Cache - Reduces file I/O overhead
  • πŸ”“ Optional Auth - No keys file = no authentication required

Usage:

# Authenticated request
curl -H "X-API-Key: prod-key-abc123" http://localhost:3000/users

# Whitelisted path (no key required)
curl http://localhost:3000/health

Rate Limiting

Built-in token bucket algorithm protects against abuse:

  • Disabled by default - Enable per endpoint as needed
  • Per-endpoint configuration - Different limits for different endpoints
  • 100 tokens per IP (configurable)
  • Refills at 10 tokens/second (configurable)
  • Automatic cleanup of inactive IPs
  • 429 Too Many Requests response when limit exceeded

Enable rate limiting by exporting a rateLimit configuration in your endpoint:

export const rateLimit = {
    enabled: true,
    maxTokens: 100,
    refillRate: 10,
    refillInterval: 1000,
};

⚑ Performance & Reliability

Production Features

Feature Description
Error Boundaries Endpoint crashes are isolatedβ€”server stays healthy
30s Timeout Automatic termination of long-running requests
Atomic Writes Temp file + atomic rename prevents data corruption
File Locks Mutex-based locking prevents concurrent write issues
Graceful Shutdown Waits for in-flight requests before stopping
Hot Reload File watcher detects changes, reloads instantly
LRU Cache Smart module caching with auto-eviction (100 entry limit)
Cross-Runtime Zero-overhead abstraction for Bun and Node.js

How It Works

graph LR
    A[Request] --> B[Server]
    B --> C{Auth Check}
    C -->|Valid| D[Route to Endpoint]
    C -->|Invalid| E[401 Unauthorized]
    D --> F{Module Cached?}
    F -->|Yes| G[Execute]
    F -->|No| H[Import & Cache]
    H --> G
    G --> I[Response]
Loading

Technical Implementation:

  1. Module Caching - Endpoints cached until file changes detected
  2. File Watching - fs.watch() monitors endpoint files for changes
  3. Version Busting - import(path?v=N) forces fresh imports after edits
  4. Atomic Writes - Write to .tmp β†’ fs.rename() (POSIX guarantees atomicity)
  5. Mutex Locks - Per-file write queue prevents race conditions
  6. LRU Eviction - Least-recently-used modules evicted when cache reaches 100 entries

πŸ“ Project Structure

your-project/
β”œβ”€β”€ data/                       # Data directory (customizable)
β”‚   β”œβ”€β”€ apikeys.json           # Optional: API authentication config
β”‚   β”œβ”€β”€ database.lock          # Auto-generated: directory structure cache
β”‚   β”œβ”€β”€ Endpoints/             # Your API endpoints
β”‚   β”‚   β”œβ”€β”€ GET/
β”‚   β”‚   β”‚   β”œβ”€β”€ users.ts       # GET /users
β”‚   β”‚   β”‚   β”œβ”€β”€ users/
β”‚   β”‚   β”‚   β”‚   └── profile.ts # GET /users/profile
β”‚   β”‚   β”‚   └── health.ts      # GET /health
β”‚   β”‚   └── POST/
β”‚   β”‚       β”œβ”€β”€ users.ts       # POST /users
β”‚   β”‚       └── auth/
β”‚   β”‚           └── login.ts   # POST /auth/login
β”‚   β”œβ”€β”€ users.json             # Your data (any structure)
β”‚   └── todos.json             # More data
β”œβ”€β”€ server.ts                   # Your server entry point
└── package.json

🎯 Use Cases

βœ… Perfect For

Use Case Why NanoWarp Excels
Rapid Prototyping Zero configuration, instant endpoints
Microservices Fast startup, minimal footprint (~5MB)
Webhooks Hot-swap logic without downtime
Edge Computing Minimal dependencies, cross-runtime
Version Control Endpoints are filesβ€”easy git workflows
Internal Tools Simple APIs for dashboards and automation
Learning Projects No database complexity, focus on logic

⚠️ Not Recommended For

Scenario Better Alternative
High-concurrency writes to same file PostgreSQL, MySQL, MongoDB
Complex relational queries SQLite, PostgreSQL
Multi-TB datasets Traditional DBMS with indexing
Netflix-scale traffic Distributed DB + CDN + caching layer

πŸ—οΈ Architecture

Core Components

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           NanoWarp Server               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   Server    │────│ Rate Limiter β”‚  β”‚
β”‚  β”‚  (HTTP)     β”‚    β”‚ (Token Bucket)β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚         β”‚                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Auth Layer  │────│  API Keys    β”‚  β”‚
β”‚  β”‚  (Optional) β”‚    β”‚   Cache      β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚         β”‚                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   Router    │────│Module Cache  β”‚  β”‚
β”‚  β”‚ (File-Based)β”‚    β”‚   (LRU)      β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚         β”‚                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Endpoint   │────│File Watcher  β”‚  β”‚
β”‚  β”‚  Executor   β”‚    β”‚(Hot Reload)  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚         β”‚                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”                       β”‚
β”‚  β”‚  Database   β”‚                       β”‚
β”‚  β”‚  Manager    β”‚                       β”‚
β”‚  β”‚ (File-Based)β”‚                       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🀝 Contributing

We welcome contributions! Here's how you can help:

Development Setup

# Clone the repository
git clone https://github.com/ncwardell/NanoWarp.git
cd NanoWarp

# Install dependencies
bun install

# Run tests
bun test

# Build the project
bun run build

Contributing Guidelines

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please ensure:

  • βœ… Code follows existing style conventions
  • βœ… All tests pass
  • βœ… New features include tests
  • βœ… Documentation is updated

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ™ Acknowledgments

  • Built with Bun and Node.js
  • Inspired by modern file-based frameworks
  • Community feedback and contributions

πŸ“ž Support


⬆ Back to Top

Made with ❀️ by the NanoWarp team

About

A lightning-fast, modular API framework built on Bun, featuring a compact, custom filesystem-based database for seamless data integration.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors