- π 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 with 
req, res, nextpattern - π 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+
 
Woodland follows a security-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: nosniffset automatically,helmetrecommended for comprehensive headers - β‘ Rate Limiting: Built for middleware compatibility - use 
express-rate-limitor 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. See Technical Documentation for complete assessment.
π‘ Quick Security Setup: Add essential security middleware for production deployment:
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
// Security headers
app.always(helmet());
// Rate limiting
app.always(rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
}));Stop accepting framework overhead. Most HTTP frameworks slow you down in exchange for convenience. Woodland breaks that trade-off.
π Proven Performance: Comprehensive benchmarks show Woodland outperforms 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.
# npm
npm install woodland
# yarn
yarn add woodland
# pnpm
pnpm add woodland
# Global installation for CLI
npm install -g woodlandimport {createServer} from "node:http";
import {woodland} from "woodland";
const app = 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");
});import {Woodland} from "woodland";
class MyAPI extends Woodland {
  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: new Date().toISOString()});
  }
  
  createUser(req, res) {
    // Handle user creation
    res.status(201).json({message: "User created"});
  }
}
const api = new MyAPI();- Configuration
 - Routing
 - Middleware
 - Static Files
 - CORS
 - Error Handling
 - Response Helpers
 - Event Handlers
 - Logging
 - CLI Usage
 - API Reference
 - Performance
 - Testing
 - TypeScript
 - Examples
 - Troubleshooting
 
const app = woodland({
  autoindex: false,        // Enable directory browsing
  cacheSize: 1000,        // Internal cache size
  cacheTTL: 10000,        // Cache TTL (10 seconds)
  charset: "utf-8",       // Default charset
  corsExpose: "",         // CORS exposed headers
  defaultHeaders: {},     // Default response headers
  digit: 3,              // Timing precision digits
  etags: true,           // Enable ETag generation
  indexes: [             // Index file names
    "index.htm",
    "index.html"
  ],
  logging: {
    enabled: true,       // Enable logging
    format: "%h %l %u %t \"%r\" %>s %b", // Log format
    level: "info"        // Log level
  },
  origins: [],           // CORS origins (empty array denies all cross-origin requests)
  silent: false,         // Disable default headers
  time: false           // Enable response timing
});const app = woodland({
  // Security headers
  defaultHeaders: {
    "x-content-type-options": "nosniff",
    "x-frame-options": "DENY",
    "x-xss-protection": "1; mode=block",
    "strict-transport-security": "max-age=31536000; includeSubDomains"
  },
  
  // CORS configuration
  origins: [
    "https://myapp.com",
    "https://api.myapp.com"
  ],
  corsExpose: "x-custom-header,x-request-id",
  
  // Performance tuning
  cacheSize: 5000,
  cacheTTL: 600000, // 10 minutes
  
  // Detailed logging
  logging: {
    enabled: true,
    level: "debug",
    format: "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
  },
  
  // Enable features
  time: true,
  etags: true,
  autoindex: true
});// HTTP methods
app.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 parameters
app.get("/users/:id", (req, res) => {
  const userId = req.params.id;
  res.json({id: userId});
});
// Multiple parameters
app.get("/users/:userId/posts/:postId", (req, res) => {
  const {userId, postId} = req.params;
  res.json({userId, postId});
});// RegExp patterns
app.get("/api/v[1-3]/users", (req, res) => {
  res.json({version: req.url.match(/v(\d)/)[1]});
});
// Wildcard routes
app.get("/files/(.*)", (req, res) => {
  // Serve any file under /files/
});
// Route with validation
app.get("/users/:id(\\d+)", (req, res) => {
  // Only matches numeric IDs
  res.json({id: parseInt(req.params.id)});
});// API v1 routes
const apiV1 = (req, res, next) => {
  req.version = "v1";
  next();
};
app.get("/api/v1/users", apiV1, getAllUsers);
app.post("/api/v1/users", apiV1, createUser);
// Protected routes
const authenticate = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.error(401);
  }
  // Verify token...
  next();
};
app.get("/admin/*", authenticate, adminHandler);// Global middleware
app.always((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});
// Route-specific middleware
app.get("/protected", authenticate, authorize, handler);
// β WRONG: Do NOT register error middleware with 'always'
// This will execute BEFORE route handlers, not after errors occur
app.always((error, req, res, next) => {
  if (error) {
    console.error(error);
    res.error(500);
  } else {
    next();
  }
});
// β
 CORRECT: Register error middleware with specific routes LAST
app.get("/api/users", 
  authenticate,        // Normal middleware
  authorize,          // Normal middleware
  getUserHandler,     // Route handler
  (error, req, res, next) => {  // Error middleware - LAST
    if (error) {
      console.error(error);
      res.error(500);
    } else {
      next();
    }
  }
);
// β
 CORRECT: Global error handling should be done with route patterns
app.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) should never be registered withapp.always() - Error middleware registered with 
alwayswill execute before 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 middleware last in the middleware chain for each route
 - For global error handling, use 
app.use("/(.*)", errorHandler)as the last route registration 
// Request logging
const requestLogger = (req, res, next) => {
  const start = Date.now();
  res.on("finish", () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.url} - ${res.statusCode} (${duration}ms)`);
  });
  next();
};
// Body parser
const bodyParser = async (req, res, next) => {
  if (req.method === "POST" || req.method === "PUT") {
    let body = "";
    req.on("data", chunk => body += chunk);
    req.on("end", () => {
      try {
        req.body = JSON.parse(body);
      } catch (e) {
        req.body = body;
      }
      next();
    });
  } else {
    next();
  }
};
// Rate limiting
const rateLimit = (() => {
  const requests = new Map();
  return (req, res, next) => {
    const ip = req.ip;
    const now = Date.now();
    const window = 60000; // 1 minute
    const limit = 100;
    
    if (!requests.has(ip)) {
      requests.set(ip, []);
    }
    
    const ipRequests = requests.get(ip);
    const recentRequests = ipRequests.filter(time => now - time < window);
    
    if (recentRequests.length >= limit) {
      return res.error(429);
    }
    
    recentRequests.push(now);
    requests.set(ip, recentRequests);
    next();
  };
})();// Serve files from public directory
app.files("/static", "./public");
// Serve with custom options
app.get("/downloads/(.*)", (req, res) => {
  const filename = req.params[0];
  const filepath = path.join("./downloads", filename);
  
  // Custom file serving logic
  app.serve(req, res, filename, "./downloads");
});const app = woodland({
  autoindex: true,  // Enable directory browsing
  indexes: ["index.html", "index.htm", "default.html"]
});
app.files("/", "./public");Woodland handles CORS automatically when you configure origins. Here's what you get for free:
const app = 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- Preflight Route Registration: When origins are configured, Woodland automatically registers an OPTIONS handler that responds with 204 No Content
 - CORS Headers: For valid cross-origin requests, automatically sets all required CORS headers
 - Origin Validation: Checks request origin against configured allowed origins
 - Method Detection: Access-Control-Allow-Methods reflects actual registered routes
 - Security: Empty origins array denies all CORS requests by default
 
// Conditional CORS (disable automatic CORS, use manual)
const app = woodland({
  origins: [] // Empty = no automatic CORS
});
// Dynamic origin validation (replaces automatic validation)
app.always((req, res, next) => {
  const origin = req.headers.origin;
  
  // Custom logic for origin validation
  if (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 routes
app.options("/api/special", (req, res) => {
  res.header("access-control-allow-methods", "GET,POST"); // Restrict methods
  res.header("access-control-allow-headers", "content-type"); // Restrict headers
  res.header("access-control-max-age", "86400"); // Set cache duration
  res.send("");
});Most applications only need to configure origins and corsExpose - manual CORS handling is rarely necessary.
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"
  });
});app.on("error", (req, res, err) => {
  console.error(`Error ${res.statusCode}: ${err}`);
  
  // Log to external service
  if (res.statusCode >= 500) {
    logError(err, req);
  }
});
// Global error catching
app.always((req, res, next) => {
  try {
    next();
  } catch (error) {
    res.error(500, error.message);
  }
});app.get("/users/:id", (req, res) => {
  const user = {id: req.params.id, name: "John Doe"};
  res.json(user);
});
app.post("/users", (req, res) => {
  const user = createUser(req.body);
  res.json(user, 201);
});app.get("/old-path", (req, res) => {
  res.redirect("/new-path");
});
app.get("/temporary", (req, res) => {
  res.redirect("/permanent", false); // Temporary redirect
});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"}));
});// Connection established
app.on("connect", (req, res) => {
  console.log(`Connection from ${req.ip}`);
});
// Request finished
app.on("finish", (req, res) => {
  console.log(`Request completed: ${req.method} ${req.url}`);
});
// Error occurred
app.on("error", (req, res, err) => {
  console.error(`Error: ${err}`);
});
// File streaming
app.on("stream", (req, res) => {
  console.log(`Streaming file to ${req.ip}`);
});app.on("finish", (req, res) => {
  const metrics = {
    method: req.method,
    url: req.url,
    status: res.statusCode,
    ip: req.ip,
    userAgent: req.headers["user-agent"],
    timestamp: new Date().toISOString()
  };
  
  // Send to analytics service
  analytics.track(metrics);
});error: Error messageswarn: Warning messagesinfo: Informational messagesdebug: Debug messages
const app = woodland({
  logging: {
    enabled: true,
    level: "debug",
    format: "%h %l %u %t \"%r\" %>s %b %D"
  }
});
// Manual logging
app.log("Custom message", "info");
app.log("Debug information", "debug");| Placeholder | Description | 
|---|---|
%h | 
Remote IP address | 
%l | 
Remote logname (always -) | 
%u | 
Remote user (always -) | 
%t | 
Timestamp | 
%r | 
First line of request | 
%s | 
Status code | 
%b | 
Response size | 
%{Header}i | 
Request header | 
%{Header}o | 
Response header | 
# Serve current directory
woodland
# Custom IP and port
woodland --ip=0.0.0.0 --port=3000
# Disable logging
woodland --logging=false
# Serve specific directory
cd /path/to/files && woodlandThe CLI achieves 100% test coverage with comprehensive unit tests covering argument parsing, validation, server configuration, error handling scenarios, and actual HTTP request serving verification.
| Option | Default | Description | 
|---|---|---|
--ip | 
127.0.0.1 | 
Server IP address | 
--port | 
8000 | 
Server port | 
--logging | 
true | 
Enable/disable request logging | 
$ woodland --port=3000
id=woodland, hostname=localhost, ip=127.0.0.1, port=3000
127.0.0.1 - [18/Dec/2024:10:30:00 -0500] "GET / HTTP/1.1" 200 1327
127.0.0.1 - [18/Dec/2024:10:30:05 -0500] "GET /favicon.ico HTTP/1.1" 404 9new Woodland(config)get(path, ...middleware)- GET requestspost(path, ...middleware)- POST requestsput(path, ...middleware)- PUT requestsdelete(path, ...middleware)- DELETE requestspatch(path, ...middleware)- PATCH requestsoptions(path, ...middleware)- OPTIONS requeststrace(path, ...middleware)- TRACE requestsconnect(path, ...middleware)- CONNECT requests
always(path, ...middleware)- All HTTP methods - executes before request HTTP method middlewareuse(path, ...middleware, method)- Generic middleware registrationfiles(root, folder)- Static file servingignore(fn)- Ignore middleware forAllowheaderallowed(method, uri)- Check if method is allowedallows(uri)- Get allowed methods stringlist(method, type)- List registered routeslog(message, level)- Log a message
onReady(req, res, body, status, headers)- Before sending responseonSend(req, res, body, status, headers)- Customize responseonDone(req, res, body, headers)- Finalize response
req.allow- Allowed methods for current pathreq.body- Request body (populate with middleware)req.cors- Boolean indicating CORS requestreq.corsHost- Boolean indicating "origin" and "host" request headers are in syncreq.host- Request hostnamereq.ip- Client IP addressreq.params- Route parametersreq.parsed- Parsed URL objectreq.valid- Request validation statusreq.exit()- Exit middleware chain
res.locals- Local variables objectres.error(status, body, headers)- Send error responseres.header(key, value)- Set response headerres.json(body, status, headers)- Send JSON responseres.redirect(url, permanent)- Send redirect responseres.send(body, status, headers)- Send responseres.set(headers)- Set multiple headersres.status(code)- Set status code
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 average
Platform: Apple Mac Mini M4 Pro, Node.js 24.8.0
Fastify framework:        14,283 ops/sec  (0.070ms avg)  π₯ FASTEST
Woodland framework:       12,478 ops/sec  (0.080ms avg)  π₯ Strong second
Express.js framework:     12,112 ops/sec  (0.083ms avg)  π₯ Third place
Raw 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
 
Node.js 24.8.0 (1000 iterations, 100 warmup, averaged across 5 runs)
HTTP Operations
404 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)
- Choose Woodland over alternatives: Woodland provides 15% better performance than raw Node.js and 3% better than Express.js for JSON responses
 - Enable Route Caching: Route caching provides significant performance improvement - allows() with cache: 4.8M ops/sec vs without: 300K ops/sec
 - Optimize Route Order: Place frequently accessed routes first in your application
 - Use Parameter Routes: Parameter routes perform competitively with static routes (~2.4M vs ~2.5M ops/sec)
 - Enable ETags: Reduces bandwidth for unchanged resources (333K ops/sec with ETags)
 - Stream Large Files: Use built-in streaming for files (330K ops/sec streaming performance)
 - Minimize Middleware: Only use necessary middleware - complex middleware reduces performance
 - Leverage Built-in Utilities: Use woodland's optimized utility functions (7.7M+ ops/sec for common operations)
 - Configure Appropriate Caching: Set proper cache headers and TTL values
 - Use Proper HTTP Methods: DELETE requests show best performance (15.7K ops/sec) for CRUD operations
 
git clone https://github.com/avoidwork/woodland.git
cd woodland
npm install
# Run all benchmarks
npm run benchmark
# Run specific benchmark suites
node benchmark.js routing utility serving
node benchmark.js http middleware comparison
# Run with custom settings
node benchmark.js --iterations 2000 --warmup 200
# Run specific suite with custom settings
node benchmark.js utility -i 500 -w 50Available benchmark suites:
comparison- Framework vs raw Node.js HTTP module performancehttp- End-to-end HTTP server performancemiddleware- Middleware registration and executionrouting- Route matching and resolutionserving- File serving and streamingutility- Core utility functions
Woodland maintains 100% statement coverage with comprehensive testing across all features. The CLI module achieves 100% coverage with rigorous testing of all code paths including successful server startup, and the utility module achieves 100% line coverage with comprehensive edge case testing.
npm test386 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 |                   
--------------|---------|----------|---------|---------|-------------------
- 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
 
import {woodland} from "woodland";
import assert from "node:assert";
describe("My API", () => {
  let app;
  
  beforeEach(() => {
    app = woodland();
  });
  
  it("should respond to GET /", async () => {
    app.get("/", (req, res) => res.send("Hello"));
    
    const req = {method: "GET", url: "/", headers: {}};
    const res = {
      statusCode: 200,
      headers: {},
      setHeader: (k, v) => res.headers[k] = v,
      end: (body) => res.body = body
    };
    
    app.route(req, res);
    assert.equal(res.body, "Hello");
  });
});import {Woodland, woodland} from "woodland";
import {IncomingMessage, ServerResponse} from "node:http";
// Using factory function
const app = woodland({
  defaultHeaders: {"content-type": "application/json"}
});
// Using class
class MyAPI extends Woodland {
  constructor() {
    super({time: true});
  }
}
// Custom middleware with types
interface CustomRequest extends IncomingMessage {
  user?: {id: string, name: string};
}
const authenticate = (
  req: CustomRequest,
  res: ServerResponse,
  next: () => void
): void => {
  req.user = {id: "123", name: "John"};
  next();
};interface WoodlandConfig {
  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;
}import {createServer} from "node:http";
import {woodland} from "woodland";
const app = woodland({
  defaultHeaders: {"content-type": "application/json"},
  time: true
});
const users = new Map();
// Middleware
app.always(async (req, res, next) => {
  if (req.method === "POST" || req.method === "PUT") {
    let body = "";
    req.on("data", chunk => body += chunk);
    req.on("end", () => {
      try {
        req.body = JSON.parse(body);
      } catch (e) {
        return res.error(400, "Invalid JSON");
      }
      next();
    });
  } else {
    next();
  }
});
// Routes
app.get("/users", (req, res) => {
  res.json(Array.from(users.values()));
});
app.get("/users/:id", (req, res) => {
  const user = users.get(req.params.id);
  if (!user) {
    return res.error(404, "User not found");
  }
  res.json(user);
});
app.post("/users", (req, res) => {
  const {name, email} = req.body;
  if (!name || !email) {
    return res.error(400, "Name and email required");
  }
  
  const id = Date.now().toString();
  const user = {id, name, email};
  users.set(id, user);
  res.json(user, 201);
});
app.put("/users/:id", (req, res) => {
  const user = users.get(req.params.id);
  if (!user) {
    return res.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)) {
    return res.error(404, "User not found");
  }
  
  users.delete(req.params.id);
  res.status(204).send("");
});
createServer(app.route).listen(3000);import {createServer} from "node:http";
import {woodland} from "woodland";
import {createWriteStream} from "node:fs";
import {pipeline} from "node:stream/promises";
const app = woodland();
app.post("/upload", async (req, res) => {
  try {
    const filename = req.headers["x-filename"] || "upload.bin";
    const writeStream = createWriteStream(`./uploads/${filename}`);
    
    await pipeline(req, writeStream);
    res.json({message: "Upload successful", filename});
  } catch (error) {
    res.error(500, "Upload failed");
  }
});
createServer(app.route).listen(3000);import {createServer} from "node:http";
import {WebSocketServer} from "ws";
import {woodland} from "woodland";
const app = woodland();
const server = createServer(app.route);
const wss = new WebSocketServer({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);// Problem: CORS blocked requests
// Solution: Configure origins properly
const app = woodland({
  origins: ["https://myapp.com", "http://localhost:3000"]
});// Problem: Routes not matching
// Solution: Check route patterns
app.get("/users/:id", handler);        // β
 Correct
app.get("/users/:id/", handler);       // β Trailing slash
app.get("/users/([0-9]+)", handler);   // β
 RegExp pattern// 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 map
app.always(requestLogger);      // Added to WILDCARD middleware map  
app.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 first
app.always(securityHeaders);   // Always middleware registered after
app.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// Problem: High memory usage
// Solution: Tune cache settings
const app = woodland({
  cacheSize: 100,    // Reduce cache size
  cacheTTL: 60000   // Shorter TTL
});const app = woodland({
  logging: {
    enabled: true,
    level: "debug"
  }
});
// Enable debug logs
app.log("Debug message", "debug");- Check middleware overhead: Profile middleware execution
 - Optimize route patterns: Use specific patterns vs wildcards
 - Enable caching: Use ETags and cache headers
 - Monitor memory: Watch for memory leaks in long-running apps
 
Copyright (c) 2025 Jason Mulligan
Licensed under the BSD-3-Clause license.
- Fork the repository
 - Create a feature branch
 - Add tests for new functionality
 - Ensure all tests pass
 - Submit a pull request
 
- Issues: GitHub Issues
 - Documentation: GitHub Wiki
 - Discussions: GitHub Discussions