Skip to content

ishtms/velocy

Repository files navigation

Velocy

A blazing fast, zero-dependency HTTP framework for Node.js

npm version License: MIT

Velocy Benchmark

Features

  • Blazing Fast - Radix tree-based router with performance optimizations
  • Zero Dependencies - Built entirely with Node.js built-in modules
  • Advanced Routing - Dynamic parameters, wildcards, and pattern matching
  • WebSocket Support - Full WebSocket implementation with rooms and broadcasting
  • Middleware System - Express-style middleware with async support
  • Template Engines - Built-in template engine support with caching
  • Security Features - CORS, rate limiting, session management, and validation
  • Performance Monitoring - Built-in performance tracking and optimization
  • Compression - Gzip, deflate, and brotli compression support
  • Cookie Management - Cookie parsing and signing capabilities

Performance

Velocy is 5.56x faster than Express based on our latest benchmarks:

Framework Req/Sec Avg Latency Transfer Rate
Velocy 91,330 1.40ms 16.11 MB/s
Express 16,438 7.78ms 3.76 MB/s
  • 5.56x more requests per second
  • 5.56x lower average latency
  • 4.28x higher transfer rate
  • Zero dependencies for maximum efficiency

Benchmarked using rewrk with 128 connections on Node.js v20.x LTS

Installation

npm install velocy
# or
yarn add velocy
# or
pnpm add velocy

Quick Start

const { Router, createServer } = require("velocy");

const app = new Router();

// Basic route
app.get("/", (req, res) => {
  res.json({ message: "Hello, Velocy!" });
});

// Dynamic parameters
app.get("/users/:id", (req, res) => {
  res.json({ userId: req.params.id });
});

// Start server
createServer(app).listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

Example Servers

Velocy includes a fully-functional server implementations to help you get started:

📚 Example Server (example_server.js)

A comprehensive reference implementation showcasing everything Velocy can do:

  • ALL framework features demonstrated
  • Every HTTP method, router type, and middleware pattern
  • Advanced WebSocket with rooms
  • View engine integration
  • Performance monitoring
  • Extensive inline documentation

Run it: node example_server.js

Table of Contents

Routing

Basic Routing

const { Router } = require("velocy");
const app = new Router();

// HTTP methods
app.get("/users", (req, res) => res.json({ users: [] }));
app.post("/users", (req, res) => res.json({ created: true }));
app.put("/users/:id", (req, res) => res.json({ updated: req.params.id }));
app.delete("/users/:id", (req, res) => res.json({ deleted: req.params.id }));
app.patch("/users/:id", (req, res) => res.json({ patched: req.params.id }));

// Route chaining
app.get("/posts", listPosts).post("/posts", createPost).get("/posts/:id", getPost);

Dynamic Parameters

// Single parameter
app.get("/users/:id", (req, res) => {
  res.json({ userId: req.params.id });
});

// Multiple parameters
app.get("/users/:userId/posts/:postId", (req, res) => {
  res.json({
    userId: req.params.userId,
    postId: req.params.postId,
  });
});

// Optional parameters with wildcards
app.get("/files/*", (req, res) => {
  res.json({ file: req.params["*"] });
});

Wildcards

// Single-segment wildcard (*)
app.get("/static/*.js", (req, res) => {
  res.json({ jsFile: req.params["*"] });
});

// Multi-segment wildcard (**)
app.get("/api/**", (req, res) => {
  res.json({ path: req.params["**"] });
});

// Named wildcards
app.get("/assets/*filename", (req, res) => {
  res.json({ filename: req.params.filename });
});

Router Merging and Nesting

// Create modular routers
const userRouter = new Router();
userRouter.get("/profile", getUserProfile);
userRouter.post("/settings", updateSettings);

const apiRouter = new Router();
apiRouter.get("/status", getApiStatus);

// Merge routers
const mainRouter = new Router();
mainRouter.merge(userRouter);
mainRouter.merge(apiRouter);

// Nest routers with prefix
const app = new Router();
app.nest("/api/v1", mainRouter);
// Routes available at: /api/v1/profile, /api/v1/settings, /api/v1/status

Route Debugging

// Print the route tree for debugging
app.printTree();
// Outputs a visual representation of all registered routes

Middleware

Global Middleware

const { Router, bodyParser, cookieParser } = require("velocy");
const app = new Router();

// Apply middleware globally
app.use(cookieParser());
app.use(bodyParser.json());

// Custom middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

// Async middleware
app.use(async (req, res, next) => {
  await authenticateUser(req);
  next();
});

Path-Specific Middleware

// Apply middleware to specific paths
app.use("/admin", authenticateAdmin);
app.use("/api", rateLimiter);

// Multiple middleware
app.use("/protected", [authenticate, authorize, logAccess]);

Error Handling Middleware

// Error middleware (4 parameters)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.statusCode || 500).json({
    error: err.message || "Internal Server Error",
  });
});

Request & Response

Enhanced Request Object

app.get("/example", (req, res) => {
  // URL and query parsing
  console.log(req.url); // Full URL
  console.log(req.path); // Path without query
  console.log(req.query); // Parsed query parameters

  // Headers
  console.log(req.headers); // All headers
  console.log(req.get("content-type")); // Get specific header

  // Content negotiation
  console.log(req.accepts("json")); // Check if client accepts JSON
  console.log(req.acceptsEncodings(["gzip", "deflate"]));
  console.log(req.acceptsLanguages(["en", "es"]));

  // Request info
  console.log(req.ip); // Client IP
  console.log(req.protocol); // http or https
  console.log(req.secure); // Is HTTPS?
  console.log(req.xhr); // Is AJAX request?

  // Cookies (with cookieParser middleware)
  console.log(req.cookies); // Parsed cookies
  console.log(req.signedCookies); // Signed cookies
});

Enhanced Response Object

app.get("/example", (req, res) => {
  // JSON response
  res.json({ success: true });

  // Status codes
  res.status(201).json({ created: true });

  // Redirects
  res.redirect("/new-location");
  res.redirect(301, "/permanent-redirect");

  // Headers
  res.set("X-Custom-Header", "value");
  res.set({
    "X-Header-1": "value1",
    "X-Header-2": "value2",
  });

  // Cookies
  res.cookie("session", "abc123", {
    maxAge: 900000,
    httpOnly: true,
    secure: true,
    sameSite: "strict",
  });

  // File sending
  res.sendFile("/path/to/file.pdf");
  res.download("/path/to/file.pdf", "custom-name.pdf");

  // Content type
  res.type("html").send("<h1>Hello</h1>");
  res.type("text/plain").send("Plain text");
});

WebSockets

WebSocket Server Setup

const { Router, createServer } = require("velocy");

const app = new Router({
  websocket: {
    heartbeatInterval: 30000, // Ping every 30 seconds
    heartbeatTimeout: 60000, // Close if no pong in 60 seconds
    maxPayloadSize: 10 * 1024 * 1024, // 10MB max message size
    enableQueue: true, // Queue messages for offline clients
    maxQueueSize: 100, // Max queued messages per client
  },
});

// WebSocket routes
app.ws("/chat", (ws, req) => {
  console.log("New WebSocket connection");

  ws.on("message", (data) => {
    // Echo message back
    ws.send(JSON.stringify({ echo: data }));
  });

  ws.on("close", () => {
    console.log("Connection closed");
  });
});

createServer(app).listen(3000);

WebSocket Rooms and Broadcasting

// Join/leave rooms
app.ws("/chat", (ws, req) => {
  // Join a room
  ws.join("general");

  ws.on("message", (data) => {
    const msg = JSON.parse(data);

    if (msg.type === "join") {
      ws.join(msg.room);
      ws.send(JSON.stringify({ joined: msg.room }));
    }

    if (msg.type === "leave") {
      ws.leave(msg.room);
      ws.send(JSON.stringify({ left: msg.room }));
    }

    if (msg.type === "broadcast") {
      // Broadcast to all in room
      app.wsRouter.broadcast(
        msg.room,
        JSON.stringify({
          from: ws.id,
          message: msg.text,
        }),
      );
    }
  });
});

// Broadcast to all connected clients
app.wsRouter.broadcastAll(JSON.stringify({ announcement: "Server message" }));

// Get room information
const roomClients = app.wsRouter.getRoom("general");
const allRooms = app.wsRouter.getRooms();

Template Engines

Built-in Template Engine

const { Router, createServer } = require("velocy");
const app = new Router();

// Configure views
app.set("views", "./views");
app.set("view engine", "html");
app.set("view cache", true); // Enable caching in production

// Render templates
app.get("/", (req, res) => {
  res.render("index", {
    title: "Velocy App",
    user: { name: "John" },
  });
});

// With layout
app.get("/about", (req, res) => {
  res.render("about", {
    title: "About Us",
    layout: "layouts/main",
  });
});

Custom Template Engines

// Register a custom engine
app.engine("ejs", {
  compile: (template, options) => {
    // Return compiled function
    return (locals) => {
      // Render template with locals
      return renderedHtml;
    };
  },
});

// Use the custom engine
app.set("view engine", "ejs");

Static Files

Serving Static Files

const { Router, static: staticMiddleware } = require("velocy");
const app = new Router();

// Serve static files from 'public' directory
app.use(
  "/static",
  staticMiddleware("./public", {
    index: "index.html", // Default file
    dotfiles: "ignore", // How to handle dotfiles
    etag: true, // Enable ETags
    lastModified: true, // Send Last-Modified header
    maxAge: "1d", // Cache control max-age
    immutable: true, // Add immutable directive
    fallthrough: true, // Pass to next middleware if not found
    acceptRanges: true, // Enable range requests
    cacheControl: true, // Send Cache-Control header

    // Custom headers
    setHeaders: (res, path, stat) => {
      if (path.endsWith(".pdf")) {
        res.set("Content-Disposition", "attachment");
      }
    },
  }),
);

// Multiple static directories
app.use("/assets", staticMiddleware("./assets"));
app.use("/downloads", staticMiddleware("./files"));

Body Parsing

JSON and URL-encoded

const { Router, bodyParser } = require("velocy");
const app = new Router();

// Parse JSON bodies
app.use(
  bodyParser.json({
    limit: "10mb", // Size limit
    strict: true, // Only accept arrays and objects
    reviver: null, // JSON.parse reviver function
    type: "application/json", // Content-type to parse
  }),
);

// Parse URL-encoded bodies
app.use(
  bodyParser.urlencoded({
    extended: true, // Use querystring library
    limit: "10mb", // Size limit
    parameterLimit: 1000, // Max number of parameters
    type: "application/x-www-form-urlencoded",
  }),
);

// Access parsed body
app.post("/users", (req, res) => {
  console.log(req.body); // Parsed body
  res.json({ received: req.body });
});

Multipart Form Data

// Parse multipart form data
app.use(
  bodyParser.multipart({
    uploadDir: "./uploads", // Directory for file uploads
    keepExtensions: true, // Keep file extensions
    maxFileSize: 10 * 1024 * 1024, // 10MB max file size
    maxFields: 1000, // Max number of fields
    maxFieldsSize: 20 * 1024 * 1024, // 20MB max for all fields
    hash: "md5", // Calculate file hash
    multiples: true, // Parse multiple files

    // File filter
    fileFilter: (part) => {
      // Only accept images
      return part.mimetype.startsWith("image/");
    },
  }),
);

// Handle file uploads
app.post("/upload", (req, res) => {
  console.log(req.body); // Form fields
  console.log(req.files); // Uploaded files

  res.json({
    fields: req.body,
    files: req.files,
  });
});

Raw Body

// Parse raw body
app.use(
  bodyParser.raw({
    type: "application/octet-stream",
    limit: "10mb",
  }),
);

// Parse text body
app.use(
  bodyParser.text({
    type: "text/plain",
    defaultCharset: "utf-8",
    limit: "1mb",
  }),
);

Cookies

Cookie Parsing and Setting

const { Router, cookieParser } = require("velocy");
const app = new Router();

// Enable cookie parsing with optional secret for signed cookies
app.use(cookieParser("optional-secret-key"));

// Read cookies
app.get("/cookies", (req, res) => {
  console.log(req.cookies); // Regular cookies
  console.log(req.signedCookies); // Signed cookies

  res.json({
    cookies: req.cookies,
    signed: req.signedCookies,
  });
});

// Set cookies
app.get("/set-cookie", (req, res) => {
  // Simple cookie
  res.cookie("name", "value");

  // Cookie with options
  res.cookie("session", "abc123", {
    domain: ".example.com", // Cookie domain
    path: "/", // Cookie path
    secure: true, // HTTPS only
    httpOnly: true, // Not accessible via JavaScript
    maxAge: 900000, // Max age in milliseconds
    expires: new Date(Date.now() + 900000), // Expiration date
    sameSite: "strict", // CSRF protection
    signed: true, // Sign the cookie
  });

  res.json({ set: true });
});

// Clear cookies
app.get("/clear-cookie", (req, res) => {
  res.clearCookie("name");
  res.clearCookie("session", { path: "/" });
  res.json({ cleared: true });
});

Sessions

Session Management

const { Router, session } = require("velocy");
const app = new Router();

// Configure sessions
app.use(
  session({
    secret: "keyboard-cat", // Secret for signing session ID
    resave: false, // Don't save unchanged sessions
    saveUninitialized: false, // Don't create empty sessions
    rolling: true, // Reset expiry on activity

    cookie: {
      secure: true, // HTTPS only
      httpOnly: true, // Not accessible via JavaScript
      maxAge: 1000 * 60 * 60, // 1 hour
      sameSite: "strict", // CSRF protection
    },

    name: "sessionId", // Cookie name
    genid: () => generateId(), // Custom ID generator

    store: new MemoryStore({
      // Session store (default: memory)
      checkPeriod: 86400000, // Prune expired entries every 24h
    }),
  }),
);

// Use sessions
app.get("/login", (req, res) => {
  req.session.userId = 123;
  req.session.username = "john";
  res.json({ logged_in: true });
});

app.get("/profile", (req, res) => {
  if (!req.session.userId) {
    return res.status(401).json({ error: "Not logged in" });
  }

  res.json({
    userId: req.session.userId,
    username: req.session.username,
  });
});

app.get("/logout", (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      return res.status(500).json({ error: "Could not log out" });
    }
    res.json({ logged_out: true });
  });
});

CORS

Cross-Origin Resource Sharing

const { Router, cors } = require("velocy");
const app = new Router();

// Enable CORS with default settings
app.use(cors());

// Custom CORS configuration
app.use(
  cors({
    origin: "https://example.com", // Allowed origin
    // or multiple origins
    origin: ["https://example.com", "https://app.example.com"],
    // or dynamic origin
    origin: (origin, callback) => {
      if (isAllowed(origin)) {
        callback(null, true);
      } else {
        callback(new Error("Not allowed by CORS"));
      }
    },

    methods: ["GET", "POST", "PUT", "DELETE"], // Allowed methods
    allowedHeaders: ["Content-Type", "Authorization"], // Allowed headers
    exposedHeaders: ["Content-Length", "X-Token"], // Headers exposed to client
    credentials: true, // Allow credentials
    maxAge: 86400, // Preflight cache duration
    preflightContinue: false, // Pass preflight to next handler
    optionsSuccessStatus: 204, // Status for successful OPTIONS
  }),
);

// CORS for specific routes
app.get("/api/public", cors(), handler);

// Different CORS per route
const corsOptions = {
  origin: "https://trusted.com",
  credentials: true,
};
app.get("/api/private", cors(corsOptions), handler);

Compression

Response Compression

const { Router, compression } = require("velocy");
const app = new Router();

// Enable compression with default settings
app.use(compression());

// Custom compression configuration
app.use(
  compression({
    threshold: 1024, // Min size to compress (bytes)
    level: 6, // Compression level (0-9)
    memLevel: 8, // Memory level (1-9)
    strategy: 0, // Compression strategy
    chunkSize: 16384, // Chunk size
    windowBits: 15, // Window bits

    filter: (req, res) => {
      // Custom logic to determine if response should be compressed
      if (req.headers["x-no-compression"]) {
        return false;
      }
      return compression.filter(req, res);
    },

    // Brotli options
    brotli: {
      enabled: true,
      quality: 4, // Brotli quality (0-11)
      lgwin: 22, // Brotli window size
    },
  }),
);

Rate Limiting

Request Rate Limiting

const { Router, rateLimit } = require("velocy");
const app = new Router();

// Basic rate limiting
app.use(
  rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // Max 100 requests per window
    message: "Too many requests", // Error message
    statusCode: 429, // Error status code
    headers: true, // Send rate limit headers
    skipSuccessfulRequests: false, // Count all requests
    skipFailedRequests: false, // Count failed requests

    // Custom key generator
    keyGenerator: (req) => {
      return req.ip; // Default: use IP address
    },

    // Custom handler
    handler: (req, res) => {
      res.status(429).json({
        error: "Too many requests",
        retryAfter: req.rateLimit.resetTime,
      });
    },

    // Skip certain requests
    skip: (req) => {
      return req.ip === "127.0.0.1";
    },
  }),
);

// Different limits for different routes
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
});

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  skipSuccessfulRequests: true,
});

app.use("/api", apiLimiter);
app.use("/auth/login", authLimiter);

Validation

Request Validation

const { Router, validator } = require("velocy");
const app = new Router();

// Validate request body
app.post(
  "/users",
  validator.body({
    username: {
      type: "string",
      required: true,
      minLength: 3,
      maxLength: 20,
      pattern: /^[a-zA-Z0-9_]+$/,
    },
    email: {
      type: "string",
      required: true,
      format: "email",
    },
    age: {
      type: "number",
      min: 18,
      max: 120,
    },
    roles: {
      type: "array",
      items: {
        type: "string",
        enum: ["user", "admin", "moderator"],
      },
    },
  }),
  (req, res) => {
    // Request body is validated
    res.json({ user: req.body });
  },
);

// Validate query parameters
app.get(
  "/search",
  validator.query({
    q: {
      type: "string",
      required: true,
      minLength: 1,
    },
    limit: {
      type: "number",
      default: 10,
      min: 1,
      max: 100,
    },
    offset: {
      type: "number",
      default: 0,
      min: 0,
    },
  }),
  (req, res) => {
    res.json({ results: [] });
  },
);

// Validate route parameters
app.get(
  "/users/:id",
  validator.params({
    id: {
      type: "string",
      pattern: /^[0-9a-f]{24}$/, // MongoDB ObjectId
    },
  }),
  (req, res) => {
    res.json({ userId: req.params.id });
  },
);

// Custom validation
app.post(
  "/register",
  validator.custom(async (req) => {
    if (req.body.password !== req.body.confirmPassword) {
      throw new Error("Passwords do not match");
    }

    const userExists = await checkUserExists(req.body.email);
    if (userExists) {
      throw new Error("Email already registered");
    }

    return true;
  }),
  (req, res) => {
    res.json({ registered: true });
  },
);

// Sanitization
app.post(
  "/comment",
  validator.body({
    text: {
      type: "string",
      required: true,
      sanitize: {
        trim: true,
        escape: true,
        lowercase: false,
      },
    },
  }),
  (req, res) => {
    res.json({ comment: req.body.text });
  },
);

Performance Optimization

Enable Performance Features

const { Router } = require("velocy");

// Create router with performance optimizations
const app = new Router({
  // Caching
  cache: true, // Enable route caching
  routeCacheSize: 1000, // Cache up to 1000 routes
  urlCacheSize: 500, // Cache up to 500 parsed URLs

  // Performance monitoring
  performance: {
    enabled: true, // Enable performance tracking
    windowSize: 60000, // 1-minute window for metrics
  },

  // Object pooling
  enablePooling: true, // Enable object pooling
  poolSize: 100, // Pool size for reusable objects

  // Other optimizations
  trustProxy: true, // Trust proxy headers
  mergeParams: true, // Merge params from parent router
});

// Monitor performance
app.on("performance", (metrics) => {
  console.log("Requests/sec:", metrics.requestsPerSecond);
  console.log("Avg response time:", metrics.avgResponseTime);
  console.log("Cache hit rate:", metrics.cacheHitRate);
});

// Performance hooks
app.hook("beforeRoute", (req) => {
  req.startTime = Date.now();
});

app.hook("afterRoute", (req, res) => {
  const duration = Date.now() - req.startTime;
  res.set("X-Response-Time", `${duration}ms`);
});

Benchmarking

// Run benchmarks
const { benchmark } = require("velocy/benchmark");

benchmark({
  routes: app,
  requests: 10000,
  concurrent: 100,
  warmup: 1000,
}).then((results) => {
  console.log("Throughput:", results.throughput);
  console.log("Latency p50:", results.latency.p50);
  console.log("Latency p99:", results.latency.p99);
});

API Reference

Router Class

class Router {
  // HTTP methods
  get(path, ...handlers)
  post(path, ...handlers)
  put(path, ...handlers)
  delete(path, ...handlers)
  patch(path, ...handlers)
  head(path, ...handlers)
  options(path, ...handlers)
  all(path, ...handlers)

  // Middleware
  use(...middleware)
  use(path, ...middleware)

  // Router composition
  merge(router)
  nest(prefix, router)

  // WebSocket
  ws(path, handler)

  // Settings
  set(setting, value)
  get(setting)
  enable(setting)
  disable(setting)
  enabled(setting)
  disabled(setting)

  // Template engines
  engine(ext, engine)
  render(view, locals, callback)

  // Events
  on(event, handler)
  off(event, handler)
  emit(event, ...args)

  // Utilities
  printTree()
  getRoutes()
  clearCache()
}

Request Object

interface Request {
  // Properties
  app: Router
  baseUrl: string
  body: any
  cookies: object
  fresh: boolean
  hostname: string
  ip: string
  ips: string[]
  method: string
  originalUrl: string
  params: object
  path: string
  protocol: string
  query: object
  route: object
  secure: boolean
  signedCookies: object
  stale: boolean
  subdomains: string[]
  xhr: boolean

  // Methods
  accepts(types: string|string[]): string|false
  acceptsCharsets(charsets: string|string[]): string|false
  acceptsEncodings(encodings: string|string[]): string|false
  acceptsLanguages(languages: string|string[]): string|false
  get(header: string): string
  is(type: string|string[]): string|false
  range(size: number): object|number|string
}

Response Object

interface Response {
  // Properties
  app: Router
  headersSent: boolean
  locals: object
  statusCode: number
  statusMessage: string

  // Methods
  append(header: string, value: string|string[]): Response
  attachment(filename?: string): Response
  clearCookie(name: string, options?: object): Response
  contentType(type: string): Response
  cookie(name: string, value: any, options?: object): Response
  download(path: string, filename?: string, callback?: Function): void
  end(data?: any, encoding?: string): void
  format(object: object): Response
  get(header: string): string
  json(object: any): Response
  jsonp(object: any): Response
  links(links: object): Response
  location(url: string): Response
  redirect(status?: number, url?: string): void
  render(view: string, locals?: object, callback?: Function): void
  send(body: any): Response
  sendFile(path: string, options?: object, callback?: Function): void
  sendStatus(statusCode: number): Response
  set(header: string|object, value?: string): Response
  status(statusCode: number): Response
  type(type: string): Response
  vary(header: string): Response
}

Examples

Complete Application Example

const {
  Router,
  createServer,
  bodyParser,
  cookieParser,
  session,
  cors,
  compression,
  rateLimit,
  static: staticMiddleware,
} = require("velocy");

// Create app with optimizations
const app = new Router({
  cache: true,
  performance: true,
});

// Global middleware
app.use(compression());
app.use(cors());
app.use(cookieParser("secret"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(
  session({
    secret: "keyboard cat",
    resave: false,
    saveUninitialized: false,
  }),
);

// Rate limiting
app.use(
  "/api",
  rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100,
  }),
);

// Static files
app.use("/public", staticMiddleware("./public"));

// View engine
app.set("views", "./views");
app.set("view engine", "html");

// Routes
app.get("/", (req, res) => {
  res.render("index", { title: "Home" });
});

app.get("/api/users", (req, res) => {
  res.json({ users: [] });
});

app.post("/api/users", (req, res) => {
  // Create user
  res.status(201).json({ created: true });
});

// WebSocket
app.ws("/ws", (ws, req) => {
  ws.on("message", (msg) => {
    ws.send(`Echo: ${msg}`);
  });
});

// Error handling
app.use((err, req, res, next) => {
  res.status(500).json({ error: err.message });
});

// Start server
const server = createServer(app);
server.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT © Ishtmeet Singh

Support

Links


Made with ❤️ by Ishtmeet

About

A blazing fast, minimal backend framework for Node.js with 0 depenencies!

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published