A lightweight, zero-dependency development server designed for full-stack developers. Features static file serving, live reload, SPA fallback, automatic HTTPS with mkcert detection, powerful proxy support with WebSocket forwarding, and a flexible plugin system.
Find a file
Ersin KOÇ a0057c1a35 init
2026-01-15 11:39:58 +02:00
.serena init 2026-01-15 11:39:58 +02:00
examples init 2026-01-15 11:39:58 +02:00
src init 2026-01-15 11:39:58 +02:00
tests init 2026-01-15 11:39:58 +02:00
.gitignore init 2026-01-15 11:39:58 +02:00
.prettierrc init 2026-01-15 11:39:58 +02:00
eslint.config.js init 2026-01-15 11:39:58 +02:00
IMPLEMENTATION.md init 2026-01-15 11:39:58 +02:00
llms.txt init 2026-01-15 11:39:58 +02:00
oxog-serve-prompt.md init 2026-01-15 11:39:58 +02:00
package-lock.json init 2026-01-15 11:39:58 +02:00
package.json init 2026-01-15 11:39:58 +02:00
README.md init 2026-01-15 11:39:58 +02:00
SPECIFICATION.md init 2026-01-15 11:39:58 +02:00
TASKS.md init 2026-01-15 11:39:58 +02:00
tsconfig.json init 2026-01-15 11:39:58 +02:00
tsup.config.ts init 2026-01-15 11:39:58 +02:00
vitest.config.ts init 2026-01-15 11:39:58 +02:00

@oxog/serve

Zero-config development server with proxy, HMR, and auto-HTTPS

A lightweight, zero-dependency development server designed for full-stack developers. Features static file serving, live reload, SPA fallback, automatic HTTPS with mkcert detection, powerful proxy support with WebSocket forwarding, and a flexible plugin system.

Installation

npm install @oxog/serve

Quick Start

import { serve } from '@oxog/serve';

// Minimal usage
const server = await serve('./dist');

// Full configuration
const server = await serve({
  root: './dist',
  port: 3000,
  spa: true,
  https: true,
  proxy: {
    '/api': 'http://localhost:8080'
  }
});

CLI Usage

# Serve current directory
npx @oxog/serve

# Serve specific directory
npx @oxog/serve ./dist

# With options
npx @oxog/serve ./dist --port 3000 --spa --open

# HTTPS with CORS
npx @oxog/serve ./dist --https --cors

# Proxy to backend
npx @oxog/serve ./dist --proxy.api=http://localhost:8080

Features

Static File Serving

Serve files with proper MIME types, ETags, and caching headers.

const server = await serve({
  root: './dist',
  port: 3000
});

Live Reload

Automatic browser refresh on file changes with CSS hot reload.

const server = await serve({
  root: './dist',
  livereload: {
    debounce: 100,
    cssReload: true
  }
});

SPA Fallback

Support for single-page applications with history API.

const server = await serve({
  root: './dist',
  spa: true,
  // or with options:
  spa: {
    fallback: 'index.html',
    ignore: ['/api', '/assets']
  }
});

HTTP/WebSocket Proxy

Forward requests to backend servers.

const server = await serve({
  root: './dist',
  proxy: {
    '/api': 'http://localhost:8080',
    '/ws': {
      target: 'http://localhost:8080',
      ws: true,
      rewrite: (path) => path.replace('/ws', '')
    }
  }
});

Auto-HTTPS

Automatic certificate generation with mkcert support.

const server = await serve({
  root: './dist',
  https: true,
  // or with mkcert:
  https: { mkcert: true }
});

CORS

Enable Cross-Origin Resource Sharing for development.

const server = await serve({
  root: './dist',
  cors: true,
  // or with options:
  cors: {
    origin: ['http://localhost:4200'],
    credentials: true
  }
});

Compression

Gzip and Brotli compression.

const server = await serve({
  root: './dist',
  compression: true
});

Directory Listing

Auto-generated file listing for directories.

const server = await serve({
  root: './files',
  directory: {
    icons: true,
    sort: 'name'
  }
});

Server API

const server = await serve('./dist');

// Get address info
const { host, port, protocol } = server.address();

// Subscribe to events
server.on('request', (req, res) => console.log(req.url));
server.on('change', (path) => console.log('Changed:', path));
server.on('error', (err) => console.error(err));

// Trigger manual reload
server.reload();

// Stop server
await server.close();

// Restart server
await server.restart();

// Add plugin at runtime
server.use(myPlugin);

// Print server URLs
server.printUrls();

Custom Plugins

Create plugins to extend server functionality.

import { serve, type Plugin } from '@oxog/serve';

const loggerPlugin: Plugin = {
  name: 'logger',
  version: '1.0.0',

  install(kernel) {
    kernel.middleware(async (req, res, next) => {
      console.log(`${req.method} ${req.url}`);
      await next();
    }, 1); // Priority 1 (runs first)
  }
};

const server = await serve({
  root: './dist',
  plugins: [loggerPlugin]
});

CLI Options

Usage: serve [directory] [options]

Arguments:
  directory              Directory to serve (default: '.')

Options:
  -p, --port <port>      Port number (default: 3000)
  -h, --host <host>      Host to bind (default: localhost)
  --spa                  Enable SPA fallback
  --https                Enable HTTPS
  --cors                 Enable CORS
  --compression          Enable compression
  --directory            Enable directory listing
  -o, --open [path]      Open browser on start
  --no-livereload        Disable live reload
  --proxy.<path>=<url>   Add proxy rule
  --help                 Show help
  --version              Show version

Configuration Options

Option Type Default Description
root string '.' Root directory to serve
port number 3000 Port number
host string 'localhost' Host to bind
livereload boolean | object true Enable live reload
spa boolean | object false Enable SPA fallback
https boolean | object false Enable HTTPS
cors boolean | object false Enable CORS
compression boolean | object false Enable compression
directory boolean | object false Enable directory listing
open boolean | string false Open browser
proxy object - Proxy configuration
plugins Plugin[] [] Additional plugins

Error Handling

import { serve, ServeError, isServeError } from '@oxog/serve';

try {
  await serve({ port: 3000 });
} catch (err) {
  if (isServeError(err)) {
    switch (err.code) {
      case 'PORT_IN_USE':
        console.log('Port is busy, try another');
        break;
      case 'ROOT_NOT_FOUND':
        console.log('Directory not found');
        break;
    }
  }
}

Requirements

  • Node.js >= 18
  • Zero runtime dependencies

License

MIT - Ersin Koc