Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

High-performance HTTP framework

License

NotificationsYou must be signed in to change notification settings

avoidwork/woodland

Repository files navigation

Woodland Logo

Woodland

High-performance HTTP framework

npm versionNode.js VersionLicenseBuild StatusTest Coverage

🚀 Features

  • 🏆 Performance Leader:15% faster than raw Node.js, 3% faster than Express.js, 87% of Fastify's performance - proven by benchmarks
  • ⚡ Zero Overhead: Framework features with performance gains, not costs
  • 🔒 Security First: Built-in CORS, ETags, and comprehensive security headers
  • 🛤️ Smart Routing: Parameter syntax (/users/:id) and RegExp support with caching
  • 🔧 Express Compatible: Familiar middleware withreq, res, next pattern
  • 📁 File Serving: High-performance static file server with streaming
  • 📘 TypeScript Ready: Full TypeScript definitions included
  • 📊 Production Logging: Common Log Format with customizable levels
  • 🚀 Modern Architecture: ES6+ modules optimized for Node.js 17+

🔒 Security & OWASP Compliance

Woodland follows asecurity-first design philosophy with strong adherence to OWASP guidelines:

  • ✅ Injection Prevention: Comprehensive input validation, HTML escaping, and path traversal protection
  • ✅ Secure Defaults: CORS disabled by default, autoindex disabled, secure error handling
  • ✅ Access Control: Strict file access controls and allowlist-based CORS validation
  • ✅ XSS Protection: All user input properly escaped, security headers included
  • 🛡️ Security Headers:X-Content-Type-Options: nosniff set automatically,helmet recommended for comprehensive headers
  • ⚡ Rate Limiting: Built for middleware compatibility - useexpress-rate-limit or similar
  • 🔍 Comprehensive Testing: 100+ dedicated security tests covering attack vectors and edge cases

OWASP Top 10 Coverage: Excellent protection against injection attacks, broken access control, security misconfigurations, and cross-site scripting. SeeTechnical Documentation for complete assessment.

💡 Quick Security Setup: Add essential security middleware for production deployment:

importhelmetfrom'helmet';importrateLimitfrom'express-rate-limit';// Security headersapp.always(helmet());// Rate limitingapp.always(rateLimit({windowMs:15*60*1000,// 15 minutesmax:100// limit each IP to 100 requests per windowMs}));

💡 Why Choose Woodland?

Stop accepting framework overhead. Most HTTP frameworks slow you down in exchange for convenience. Woodland breaks that trade-off.

🏆Proven Performance: Comprehensive benchmarks show Woodlandoutperforms raw Node.js by 15%, Express.js by 3%, and delivers 87% of Fastify's performance
Zero Compromise: Get all the framework features you need with better performance than hand-coding
🚀Battle-Tested: 100% statement coverage with 416 comprehensive tests, production-ready security, and enterprise-grade reliability
🔧Developer Experience: Express-compatible API means zero learning curve for your team

The Result? Your applications run faster, your servers handle more traffic, and your infrastructure costs less.

📦 Installation

# npmnpm install woodland# yarnyarn add woodland# pnpmpnpm add woodland# Global installation for CLInpm install -g woodland

🚀 Quick Start

Basic Server

import{createServer}from"node:http";import{woodland}from"woodland";constapp=woodland({defaultHeaders:{"cache-control":"public, max-age=3600","content-type":"text/plain"},time:true});app.get("/",(req,res)=>{res.send("Hello World!");});app.get("/users/:id",(req,res)=>{res.json({id:req.params.id,name:`User${req.params.id}`});});createServer(app.route).listen(3000,()=>{console.log("Server running on http://localhost:3000");});

Using the Class

import{Woodland}from"woodland";classMyAPIextendsWoodland{constructor(){super({defaultHeaders:{"x-api-version":"1.0.0"},origins:["https://myapp.com"]});this.setupRoutes();}setupRoutes(){this.get("/api/health",this.healthCheck);this.post("/api/users",this.createUser);}healthCheck(req,res){res.json({status:"healthy",timestamp:newDate().toISOString()});}createUser(req,res){// Handle user creationres.status(201).json({message:"User created"});}}constapi=newMyAPI();

📖 Table of Contents

⚙️ Configuration

Default Configuration

constapp=woodland({autoindex:false,// Enable directory browsingcacheSize:1000,// Internal cache sizecacheTTL:10000,// Cache TTL (10 seconds)charset:"utf-8",// Default charsetcorsExpose:"",// CORS exposed headersdefaultHeaders:{},// Default response headersdigit:3,// Timing precision digitsetags:true,// Enable ETag generationindexes:[// Index file names"index.htm","index.html"],logging:{enabled:true,// Enable loggingformat:"%h %l %u %t \"%r\" %>s %b",// Log formatlevel:"info"// Log level},origins:[],// CORS origins (empty array denies all cross-origin requests)silent:false,// Disable default headerstime:false// Enable response timing});

Advanced Configuration

constapp=woodland({// Security headersdefaultHeaders:{"x-content-type-options":"nosniff","x-frame-options":"DENY","x-xss-protection":"1; mode=block","strict-transport-security":"max-age=31536000; includeSubDomains"},// CORS configurationorigins:["https://myapp.com","https://api.myapp.com"],corsExpose:"x-custom-header,x-request-id",// Performance tuningcacheSize:5000,cacheTTL:600000,// 10 minutes// Detailed logginglogging:{enabled:true,level:"debug",format:"%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""},// Enable featurestime:true,etags:true,autoindex:true});

🛤️ Routing

Basic Routes

// HTTP methodsapp.get("/users",getAllUsers);app.post("/users",createUser);app.put("/users/:id",updateUser);app.delete("/users/:id",deleteUser);app.patch("/users/:id",patchUser);app.options("/users",optionsHandler);// Route parametersapp.get("/users/:id",(req,res)=>{constuserId=req.params.id;res.json({id:userId});});// Multiple parametersapp.get("/users/:userId/posts/:postId",(req,res)=>{const{userId, postId}=req.params;res.json({userId, postId});});

Advanced Routing

// RegExp patternsapp.get("/api/v[1-3]/users",(req,res)=>{res.json({version:req.url.match(/v(\d)/)[1]});});// Wildcard routesapp.get("/files/(.*)",(req,res)=>{// Serve any file under /files/});// Route with validationapp.get("/users/:id(\\d+)",(req,res)=>{// Only matches numeric IDsres.json({id:parseInt(req.params.id)});});

Route Groups

// API v1 routesconstapiV1=(req,res,next)=>{req.version="v1";next();};app.get("/api/v1/users",apiV1,getAllUsers);app.post("/api/v1/users",apiV1,createUser);// Protected routesconstauthenticate=(req,res,next)=>{consttoken=req.headers.authorization;if(!token){returnres.error(401);}// Verify token...next();};app.get("/admin/*",authenticate,adminHandler);

🔧 Middleware

Basic Middleware

// Global middlewareapp.always((req,res,next)=>{console.log(`${req.method}${req.url}`);next();});// Route-specific middlewareapp.get("/protected",authenticate,authorize,handler);// ❌ WRONG: Do NOT register error middleware with 'always'// This will execute BEFORE route handlers, not after errors occurapp.always((error,req,res,next)=>{if(error){console.error(error);res.error(500);}else{next();}});// ✅ CORRECT: Register error middleware with specific routes LASTapp.get("/api/users",authenticate,// Normal middlewareauthorize,// Normal middlewaregetUserHandler,// Route handler(error,req,res,next)=>{// Error middleware - LASTif(error){console.error(error);res.error(500);}else{next();}});// ✅ CORRECT: Global error handling should be done with route patternsapp.use("/(.*)",(error,req,res,next)=>{if(error){console.error(`Global error for${req.url}:`,error);res.error(500,"Internal Server Error");}else{next();}});

Important Notes:

  • Error middleware (functions with 4 parameters:error, req, res, next) shouldnever be registered withapp.always()
  • Error middleware registered withalways will executebefore route handlers, making them ineffective for catching route errors
  • .use() without a method defaults to GET - it behaves like.get(), not like.always()
  • Always register error middlewarelast in the middleware chain for each route
  • For global error handling, useapp.use("/(.*)", errorHandler) as thelast route registration

Middleware Examples

// Request loggingconstrequestLogger=(req,res,next)=>{conststart=Date.now();res.on("finish",()=>{constduration=Date.now()-start;console.log(`${req.method}${req.url} -${res.statusCode} (${duration}ms)`);});next();};// Body parserconstbodyParser=async(req,res,next)=>{if(req.method==="POST"||req.method==="PUT"){letbody="";req.on("data",chunk=>body+=chunk);req.on("end",()=>{try{req.body=JSON.parse(body);}catch(e){req.body=body;}next();});}else{next();}};// Rate limitingconstrateLimit=(()=>{constrequests=newMap();return(req,res,next)=>{constip=req.ip;constnow=Date.now();constwindow=60000;// 1 minuteconstlimit=100;if(!requests.has(ip)){requests.set(ip,[]);}constipRequests=requests.get(ip);constrecentRequests=ipRequests.filter(time=>now-time<window);if(recentRequests.length>=limit){returnres.error(429);}recentRequests.push(now);requests.set(ip,recentRequests);next();};})();

📁 Static Files

Basic File Serving

// Serve files from public directoryapp.files("/static","./public");// Serve with custom optionsapp.get("/downloads/(.*)",(req,res)=>{constfilename=req.params[0];constfilepath=path.join("./downloads",filename);// Custom file serving logicapp.serve(req,res,filename,"./downloads");});

Directory Browsing

constapp=woodland({autoindex:true,// Enable directory browsingindexes:["index.html","index.htm","default.html"]});app.files("/","./public");

🌐 CORS

Woodland handles CORS automatically when you configure origins. Here's what you get for free:

constapp=woodland({origins:["https://myapp.com","https://api.myapp.com"],corsExpose:"x-total-count,x-page-count"});// Woodland automatically provides:// ✅ Preflight OPTIONS route for all paths// ✅ Access-Control-Allow-Origin header (set to request origin if allowed)// ✅ Access-Control-Allow-Credentials: true// ✅ Access-Control-Allow-Methods (based on registered routes)// ✅ Access-Control-Allow-Headers (for OPTIONS requests)// ✅ Access-Control-Expose-Headers (for non-OPTIONS requests)// ✅ Timing-Allow-Origin header// ✅ Origin validation and security

What Woodland Does Automatically

  1. Preflight Route Registration: When origins are configured, Woodland automatically registers an OPTIONS handler that responds with 204 No Content
  2. CORS Headers: For valid cross-origin requests, automatically sets all required CORS headers
  3. Origin Validation: Checks request origin against configured allowed origins
  4. Method Detection: Access-Control-Allow-Methods reflects actual registered routes
  5. Security: Empty origins array denies all CORS requests by default

Manual CORS Control (When Needed)

// Conditional CORS (disable automatic CORS, use manual)constapp=woodland({origins:[]// Empty = no automatic CORS});// Dynamic origin validation (replaces automatic validation)app.always((req,res,next)=>{constorigin=req.headers.origin;// Custom logic for origin validationif(isValidOriginForUser(origin,req.user)){res.header("access-control-allow-origin",origin);}next();});app.always((req,res,next)=>{if(shouldAllowCORS(req)){res.header("access-control-allow-origin",req.headers.origin);res.header("access-control-allow-credentials","true");}next();});// Override automatic behavior for specific routesapp.options("/api/special",(req,res)=>{res.header("access-control-allow-methods","GET,POST");// Restrict methodsres.header("access-control-allow-headers","content-type");// Restrict headersres.header("access-control-max-age","86400");// Set cache durationres.send("");});

Most applications only need to configureorigins andcorsExpose - manual CORS handling is rarely necessary.

❌ Error Handling

Built-in Error Handling

app.get("/error",(req,res)=>{res.error(500,"Internal Server Error");});app.get("/not-found",(req,res)=>{res.error(404);});app.get("/custom-error",(req,res)=>{res.error(400,"Bad Request",{"content-type":"application/json"});});

Custom Error Handler

app.on("error",(req,res,err)=>{console.error(`Error${res.statusCode}:${err}`);// Log to external serviceif(res.statusCode>=500){logError(err,req);}});// Global error catchingapp.always((req,res,next)=>{try{next();}catch(error){res.error(500,error.message);}});

📤 Response Helpers

JSON Responses

app.get("/users/:id",(req,res)=>{constuser={id:req.params.id,name:"John Doe"};res.json(user);});app.post("/users",(req,res)=>{constuser=createUser(req.body);res.json(user,201);});

Redirects

app.get("/old-path",(req,res)=>{res.redirect("/new-path");});app.get("/temporary",(req,res)=>{res.redirect("/permanent",false);// Temporary redirect});

Custom Headers

app.get("/api/data",(req,res)=>{res.header("x-total-count","100");res.header("x-page","1");res.json({data:[]});});app.get("/download",(req,res)=>{res.set({"content-disposition":"attachment; filename=data.json","content-type":"application/json"});res.send(JSON.stringify({data:"example"}));});

🎯 Event Handlers

Available Events

// Connection establishedapp.on("connect",(req,res)=>{console.log(`Connection from${req.ip}`);});// Request finishedapp.on("finish",(req,res)=>{console.log(`Request completed:${req.method}${req.url}`);});// Error occurredapp.on("error",(req,res,err)=>{console.error(`Error:${err}`);});// File streamingapp.on("stream",(req,res)=>{console.log(`Streaming file to${req.ip}`);});

Event-Driven Analytics

app.on("finish",(req,res)=>{constmetrics={method:req.method,url:req.url,status:res.statusCode,ip:req.ip,userAgent:req.headers["user-agent"],timestamp:newDate().toISOString()};// Send to analytics serviceanalytics.track(metrics);});

📊 Logging

Log Levels

  • error: Error messages
  • warn: Warning messages
  • info: Informational messages
  • debug: Debug messages

Custom Logging

constapp=woodland({logging:{enabled:true,level:"debug",format:"%h %l %u %t \"%r\" %>s %b %D"}});// Manual loggingapp.log("Custom message","info");app.log("Debug information","debug");

Log Format Placeholders

PlaceholderDescription
%hRemote IP address
%lRemote logname (always-)
%uRemote user (always-)
%tTimestamp
%rFirst line of request
%sStatus code
%bResponse size
%{Header}iRequest header
%{Header}oResponse header

💻 CLI Usage

Basic Usage

# Serve current directorywoodland# Custom IP and portwoodland --ip=0.0.0.0 --port=3000# Disable loggingwoodland --logging=false# Serve specific directorycd /path/to/files&& woodland

The CLI achieves100% test coverage with comprehensive unit tests covering argument parsing, validation, server configuration, error handling scenarios, and actual HTTP request serving verification.

CLI Options

OptionDefaultDescription
--ip127.0.0.1Server IP address
--port8000Server port
--loggingtrueEnable/disable request logging

Example Output

$ woodland --port=3000id=woodland, hostname=localhost, ip=127.0.0.1, port=3000127.0.0.1 - [18/Dec/2024:10:30:00 -0500]"GET / HTTP/1.1" 200 1327127.0.0.1 - [18/Dec/2024:10:30:05 -0500]"GET /favicon.ico HTTP/1.1" 404 9

📚 API Reference

Woodland Class

Constructor

newWoodland(config)

HTTP Methods

  • get(path, ...middleware) - GET requests
  • post(path, ...middleware) - POST requests
  • put(path, ...middleware) - PUT requests
  • delete(path, ...middleware) - DELETE requests
  • patch(path, ...middleware) - PATCH requests
  • options(path, ...middleware) - OPTIONS requests
  • trace(path, ...middleware) - TRACE requests
  • connect(path, ...middleware) - CONNECT requests

Utility Methods

  • always(path, ...middleware) - All HTTP methods - executes before request HTTP method middleware
  • use(path, ...middleware, method) - Generic middleware registration
  • files(root, folder) - Static file serving
  • ignore(fn) - Ignore middleware forAllow header
  • allowed(method, uri) - Check if method is allowed
  • allows(uri) - Get allowed methods string
  • list(method, type) - List registered routes
  • log(message, level) - Log a message

Lifecycle Hooks

  • onReady(req, res, body, status, headers) - Before sending response
  • onSend(req, res, body, status, headers) - Customize response
  • onDone(req, res, body, headers) - Finalize response

Request Object Extensions

  • req.allow - Allowed methods for current path
  • req.body - Request body (populate with middleware)
  • req.cors - Boolean indicating CORS request
  • req.corsHost - Boolean indicating "origin" and "host" request headers are in sync
  • req.host - Request hostname
  • req.ip - Client IP address
  • req.params - Route parameters
  • req.parsed - Parsed URL object
  • req.valid - Request validation status
  • req.exit() - Exit middleware chain

Response Object Extensions

  • res.locals - Local variables object
  • res.error(status, body, headers) - Send error response
  • res.header(key, value) - Set response header
  • res.json(body, status, headers) - Send JSON response
  • res.redirect(url, permanent) - Send redirect response
  • res.send(body, status, headers) - Send response
  • res.set(headers) - Set multiple headers
  • res.status(code) - Set status code

⚡ Performance

🏆 Framework Performance Showdown

Competitive Performance: Woodland delivers excellent performance that outperforms both raw Node.js and Express.js, while achieving 87% of Fastify's performance - a strong showing for a framework that prioritizes developer experience alongside speed.

Framework Comparison (JSON Response) - 5-run averagePlatform: Apple Mac Mini M4 Pro, Node.js 24.8.0Fastify framework:        14,283 ops/sec  (0.070ms avg)  🥇 FASTESTWoodland framework:       12,478 ops/sec  (0.080ms avg)  🥈 Strong secondExpress.js framework:     12,112 ops/sec  (0.083ms avg)  🥉 Third placeRaw Node.js HTTP module:  10,888 ops/sec  (0.092ms avg)Performance improvement: +15% faster than raw Node.js, +3% faster than Express.js, 87% of Fastify's performance

Why Woodland delivers competitive performance:

  • vs Raw Node.js: Optimized request/response pipeline that eliminates common inefficiencies (+15% faster)
  • vs Express.js: Lightweight middleware system that outperforms while maintaining developer experience (+3% faster)
  • vs Fastify: Balanced approach that trades some raw speed for enhanced usability (87% of Fastify's performance)
  • Built-in JSON response optimization with smart serialization
  • Efficient header management and intelligent caching strategies
  • Developer-friendly architecture that doesn't sacrifice performance for convenience

Benchmark Results

Node.js 24.8.0 (1000 iterations, 100 warmup, averaged across 5 runs)

HTTP Operations404 handling:        16,570 ops/sec    (0.060ms avg)Parameterized routes: 14,971 ops/sec   (0.067ms avg)Error handling:      14,859 ops/sec    (0.067ms avg)JSON response:       14,422 ops/sec    (0.069ms avg)Simple GET:          13,497 ops/sec    (0.074ms avg)Middleware chain:    12,108 ops/sec    (0.083ms avg)Large response:      814 ops/sec       (1.228ms avg)

Performance Tips

  1. Choose Woodland over alternatives: Woodland provides 15% better performance than raw Node.js and 3% better than Express.js for JSON responses
  2. Enable Route Caching: Route caching provides significant performance improvement - allows() with cache: 4.8M ops/sec vs without: 300K ops/sec
  3. Optimize Route Order: Place frequently accessed routes first in your application
  4. Use Parameter Routes: Parameter routes perform competitively with static routes (~2.4M vs ~2.5M ops/sec)
  5. Enable ETags: Reduces bandwidth for unchanged resources (333K ops/sec with ETags)
  6. Stream Large Files: Use built-in streaming for files (330K ops/sec streaming performance)
  7. Minimize Middleware: Only use necessary middleware - complex middleware reduces performance
  8. Leverage Built-in Utilities: Use woodland's optimized utility functions (7.7M+ ops/sec for common operations)
  9. Configure Appropriate Caching: Set proper cache headers and TTL values
  10. Use Proper HTTP Methods: DELETE requests show best performance (15.7K ops/sec) for CRUD operations

Running Benchmarks

git clone https://github.com/avoidwork/woodland.gitcd woodlandnpm install# Run all benchmarksnpm run benchmark# Run specific benchmark suitesnode benchmark.js routing utility servingnode benchmark.js http middleware comparison# Run with custom settingsnode benchmark.js --iterations 2000 --warmup 200# Run specific suite with custom settingsnode benchmark.js utility -i 500 -w 50

Available benchmark suites:

  • comparison - Framework vs raw Node.js HTTP module performance
  • http - End-to-end HTTP server performance
  • middleware - Middleware registration and execution
  • routing - Route matching and resolution
  • serving - File serving and streaming
  • utility - Core utility functions

🧪 Testing

Test Coverage

Woodland maintains100% statement coverage with comprehensive testing across all features. The CLI module achieves100% coverage with rigorous testing of all code paths including successful server startup, and the utility module achieves100% line coverage with comprehensive edge case testing.

npmtest

Test Results

386 passing (6s)--------------|---------|----------|---------|---------|-------------------File          | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s --------------|---------|----------|---------|---------|-------------------All files     |     100 |      100 |     100 |     100 |                    cli.js       |     100 |      100 |     100 |     100 |                    constants.js |     100 |      100 |     100 |     100 |                    utility.js   |     100 |      100 |     100 |     100 |                    woodland.js  |     100 |      100 |     100 |     100 |                   --------------|---------|----------|---------|---------|-------------------

Test Categories

  • CLI Tests (100% coverage) - Argument parsing, port/IP validation, server startup with HTTP verification, error handling, logging configuration, edge cases
  • Security Integration Tests - Path traversal protection, IP security, CORS enforcement, autoindex security, security headers
  • Constants Tests - HTTP methods, status codes, headers, content types, server info, export validation
  • Security Utility Functions - File path validation, sanitization, HTML escaping, IPv4/IPv6 validation
  • Utility Functions - Autoindex generation, status resolution, MIME detection, parameter parsing, URL processing, timing utilities
  • Woodland Core Tests - Constructor configuration, HTTP method handlers, middleware registration, routing, CORS handling
  • Stream Method Tests - File headers, different file types, range requests, ETags, binary files
  • Range Request Tests - String content, invalid ranges, streams, partial content delivery
  • Cache Functionality - Route caching, allows caching, cache eviction, permissions caching
  • Serve Method Tests - Text files, HTML files, binary files, 404 handling, directory redirection, index files, autoindex, nested paths, large files
  • Middleware Tests - Execution order, error propagation, parameterized routes, exit functionality, wildcard middleware
  • Response Helper Tests - JSON responses, redirects, header manipulation, status codes, error handling

Writing Tests

import{woodland}from"woodland";importassertfrom"node:assert";describe("My API",()=>{letapp;beforeEach(()=>{app=woodland();});it("should respond to GET /",async()=>{app.get("/",(req,res)=>res.send("Hello"));constreq={method:"GET",url:"/",headers:{}};constres={statusCode:200,headers:{},setHeader:(k,v)=>res.headers[k]=v,end:(body)=>res.body=body};app.route(req,res);assert.equal(res.body,"Hello");});});

📘 TypeScript

Type Definitions

import{Woodland,woodland}from"woodland";import{IncomingMessage,ServerResponse}from"node:http";// Using factory functionconstapp=woodland({defaultHeaders:{"content-type":"application/json"}});// Using classclassMyAPIextendsWoodland{constructor(){super({time:true});}}// Custom middleware with typesinterfaceCustomRequestextendsIncomingMessage{user?:{id:string,name:string};}constauthenticate=(req:CustomRequest,res:ServerResponse,next:()=>void):void=>{req.user={id:"123",name:"John"};next();};

Configuration Types

interfaceWoodlandConfig{autoindex?:boolean;cacheSize?:number;cacheTTL?:number;charset?:string;corsExpose?:string;defaultHeaders?:Record<string,string>;digit?:number;etags?:boolean;indexes?:string[];logging?:{enabled?:boolean;format?:string;level?:string;};origins?:string[];silent?:boolean;time?:boolean;}

🔍 Examples

REST API

import{createServer}from"node:http";import{woodland}from"woodland";constapp=woodland({defaultHeaders:{"content-type":"application/json"},time:true});constusers=newMap();// Middlewareapp.always(async(req,res,next)=>{if(req.method==="POST"||req.method==="PUT"){letbody="";req.on("data",chunk=>body+=chunk);req.on("end",()=>{try{req.body=JSON.parse(body);}catch(e){returnres.error(400,"Invalid JSON");}next();});}else{next();}});// Routesapp.get("/users",(req,res)=>{res.json(Array.from(users.values()));});app.get("/users/:id",(req,res)=>{constuser=users.get(req.params.id);if(!user){returnres.error(404,"User not found");}res.json(user);});app.post("/users",(req,res)=>{const{name, email}=req.body;if(!name||!email){returnres.error(400,"Name and email required");}constid=Date.now().toString();constuser={id, name, email};users.set(id,user);res.json(user,201);});app.put("/users/:id",(req,res)=>{constuser=users.get(req.params.id);if(!user){returnres.error(404,"User not found");}Object.assign(user,req.body);res.json(user);});app.delete("/users/:id",(req,res)=>{if(!users.has(req.params.id)){returnres.error(404,"User not found");}users.delete(req.params.id);res.status(204).send("");});createServer(app.route).listen(3000);

File Upload API

import{createServer}from"node:http";import{woodland}from"woodland";import{createWriteStream}from"node:fs";import{pipeline}from"node:stream/promises";constapp=woodland();app.post("/upload",async(req,res)=>{try{constfilename=req.headers["x-filename"]||"upload.bin";constwriteStream=createWriteStream(`./uploads/${filename}`);awaitpipeline(req,writeStream);res.json({message:"Upload successful", filename});}catch(error){res.error(500,"Upload failed");}});createServer(app.route).listen(3000);

WebSocket Integration

import{createServer}from"node:http";import{WebSocketServer}from"ws";import{woodland}from"woodland";constapp=woodland();constserver=createServer(app.route);constwss=newWebSocketServer({server});app.get("/",(req,res)=>{res.send(`    <!DOCTYPE html>    <html>      <head><title>WebSocket Test</title></head>      <body>        <script>          const ws = new WebSocket('ws://localhost:3000');          ws.onmessage = e => console.log('Received:', e.data);          ws.onopen = () => ws.send('Hello Server!');        </script>      </body>    </html>  `);});wss.on("connection",(ws)=>{ws.send("Welcome to WebSocket server!");ws.on("message",(data)=>{console.log("Received:",data.toString());});});server.listen(3000);

🔧 Troubleshooting

Common Issues

CORS Errors

// Problem: CORS blocked requests// Solution: Configure origins properlyconstapp=woodland({origins:["https://myapp.com","http://localhost:3000"]});

Route Not Found

// Problem: Routes not matching// Solution: Check route patternsapp.get("/users/:id",handler);// ✅ Correctapp.get("/users/:id/",handler);// ❌ Trailing slashapp.get("/users/([0-9]+)",handler);// ✅ RegExp pattern

Middleware Order

// The 'routes' method builds middleware execution order as follows:// 1. Always middleware (WILDCARD) - added first to the middleware array// 2. Route-specific middleware - added after all always middleware// ✅ Understanding the routes method behavior:app.always(corsHandler);// Added to WILDCARD middleware mapapp.always(requestLogger);// Added to WILDCARD middleware mapapp.post("/users",authenticate,createUser);// Added to POST middleware map// When routes("/users", "POST") is called, the middleware array becomes:// [corsHandler, requestLogger, authenticate, createUser]// with exit point set between requestLogger and authenticate// ✅ Always middleware executes first regardless of registration order:app.post("/api/users",validate,createUser);// Route registered firstapp.always(securityHeaders);// Always middleware registered afterapp.always(bodyParser);// Another always middleware// Execution order for POST /api/users:// 1. securityHeaders (always middleware)// 2. bodyParser (always middleware)// 3. validate (route middleware)// 4. createUser (route handler)// ❌ Common misconception - registration order between always/route doesn't matter:// The routes method ALWAYS puts always middleware first in the execution chain

Memory Issues

// Problem: High memory usage// Solution: Tune cache settingsconstapp=woodland({cacheSize:100,// Reduce cache sizecacheTTL:60000// Shorter TTL});

Debug Mode

constapp=woodland({logging:{enabled:true,level:"debug"}});// Enable debug logsapp.log("Debug message","debug");

Performance Issues

  1. Check middleware overhead: Profile middleware execution
  2. Optimize route patterns: Use specific patterns vs wildcards
  3. Enable caching: Use ETags and cache headers
  4. Monitor memory: Watch for memory leaks in long-running apps

📄 License

Copyright (c) 2025 Jason Mulligan

Licensed under theBSD-3-Clause license.

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

📞 Support


Built with ❤️ byJason Mulligan
# Testing pre-commit without loop

About

High-performance HTTP framework

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Contributors2

  •  
  •  

Languages


[8]ページ先頭

©2009-2025 Movatter.jp