@denysvuika/picoserve
v0.1.1
Published
TypeScript-based Node.js and Express.js server for serving static files
Maintainers
Readme
PicoServe
A lightweight TypeScript-based Node.js and Express.js server for serving static files.
Features
- 🚀 Built with TypeScript for type safety
- 📁 Serves static files from a configurable directory (defaults to
public) - ⚡ Express.js-powered web server
- 🔧 Simple configuration with environment variables and command-line arguments
- 🌐 CORS enabled for development (unrestricted access)
- 🎯 SPA (Single Page Application) support with client-side routing fallback
- 🔌 Plugin system for custom API endpoints
- 🔀 Configurable proxy support for backend APIs and authentication services
- 🔐 JWT authentication example with JWKS verification support
Quick Start
Get started in seconds with npx:
npx @denysvuika/picoserveThis will serve files from your current directory's public folder on http://localhost:4200.
Installation
Using npx (No Installation Required)
Run PicoServe instantly without installing:
npx @denysvuika/picoserveGlobal Installation
Install globally to use the psrv command anywhere:
npm install -g @denysvuika/picoserveThen run:
psrvLocal Installation
Install as a project dependency:
npm install @denysvuika/picoserveAdd to your package.json scripts:
{
"scripts": {
"serve": "psrv"
}
}Then run:
npm run serveUsage
The server will start on http://localhost:4200 by default.
Command Line Options
psrv [options]
Options:
-s, --static <dir> Static files directory (default: 'public')
-p, --proxy <path> Path to proxy configuration JSON file
-h, --help Show help messageExamples:
# Serve from a different directory
psrv -s ./build
# Use custom proxy config
psrv -p /path/to/proxy.config.json
# Combine both options
psrv -s ./dist -p ./config/proxy.json
# With custom port
PORT=3000 psrv -s ./publicEnvironment Variables
Port Configuration:
PORT=8080 npm startStatic Files Directory:
STATIC_DIR=assets npm startNote: Command-line parameters take precedence over environment variables.
Priority Order:
- Command-line parameter (
-sor--static) - Environment variable (
STATIC_DIR) - Default:
publicdirectory
CORS Configuration
The server comes with CORS (Cross-Origin Resource Sharing) enabled by default with credentials support. This is ideal for development environments where you might be running your frontend and backend on different ports, and when using authentication tokens or cookies.
Current Setup (Development):
- Reflects the request origin (allows any origin dynamically)
- Credentials enabled - supports cookies, authorization headers, and TLS client certificates
- Allows common HTTP methods (GET, POST, PUT, DELETE, PATCH, OPTIONS)
- Allows standard headers (Content-Type, Authorization, X-Requested-With)
- Caches preflight OPTIONS requests for 10 minutes
Why Credentials are Important:
- Required when using cookies for authentication
- Needed for Authorization headers to work properly with proxies
- Essential for OIDC/OAuth flows
- Allows CORS preflight (OPTIONS) requests to pass through
For Production:
If you need to restrict CORS to specific origins in production, you can modify the cors() configuration in src/server.ts:
// Restrict to specific origin
app.use(cors({
origin: 'https://yourdomain.com',
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
}));
// Or allow multiple specific origins
app.use(cors({
origin: ['https://yourdomain.com', 'https://www.yourdomain.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
}));
// For API-only servers (no credentials needed)
app.use(cors({
origin: '*', // Only works when credentials: false
credentials: false
}));Note: When credentials: true is set, you cannot use origin: '*'. The server uses origin: true which reflects the requesting origin, providing the same flexibility while supporting credentials.
Endpoints
GET /- Serves static files from the configured directoryGET /health- Health check endpoint- Custom API endpoints loaded from plugins (see below)
Note: Custom API endpoints registered via plugins have higher precedence than static files. This means if you create a plugin that serves /app.config.json, it will take priority over any static file with the same name in your public directory.
API Plugins
PicoServe includes a plugin system that automatically loads custom API endpoints from the src/api/ directory. This allows you to easily extend the server with your own backend logic without modifying the core server file.
Quick Start
Create a new file in src/api/ (e.g., my-api.ts):
import { Express } from 'express';
import { PluginConfig } from './types';
export default function (app: Express, config: PluginConfig) {
app.get('/api/my-endpoint', (req, res) => {
res.json({
message: 'Hello from my API!',
staticPath: config.staticPath,
port: config.port
});
});
}The endpoint will be automatically discovered and registered on server startup!
Plugin Configuration
All plugins receive a config object as the second parameter with access to server settings:
interface PluginConfig {
staticPath: string; // Absolute path to static files directory
staticDir: string; // Original static directory argument
port: number; // Server port
[key: string]: any; // Custom config properties
}This allows your plugins to:
- Access static files programmatically
- Read server configuration
- Share common settings across plugins
If your plugin doesn't need the config, prefix the parameter with _:
export default function (app: Express, _config: PluginConfig) {
// Plugin code that doesn't use config
}Example Plugins Included
/bff/hello- Simple greeting endpoint (public, no auth)/bff/user-data- JWT authentication example with JWKS support- Demonstrates how to decode and verify JWT tokens
- Extracts username from token claims
- Works in dev mode (decode only) or production mode (full verification with OIDC)
- Includes interactive test page at
/jwt-test.html - See JWT Authentication Guide for details
/api/example- Example CRUD endpoints with parameters
Production Notes
The plugin system works seamlessly in production:
- When you run
npm run build, all TypeScript files (including plugins) are compiled to JavaScript in thedist/directory - The loader automatically detects the environment and loads
.jsfiles in production,.tsfiles in development - Simply deploy the
dist/directory with your plugins included
For detailed documentation on creating plugins, see src/api/README.md.
Proxy Configuration
PicoServe includes built-in support for proxying requests to external services. This is particularly useful for:
- Proxying authentication requests to OIDC providers
- Forwarding API requests to backend services
- Avoiding CORS issues in development
- Routing requests to microservices
Quick Start
- Create a
proxy.config.jsonfile. You can either:- Place it in your static directory (e.g.,
public/proxy.config.json) - detected automatically - Place it anywhere and specify the path with
-pparameter (e.g.,node dist/server.js -p ./config/proxy.json)
- Place it in your static directory (e.g.,
Example proxy.config.json:
{
"proxies": [
{
"path": "/auth",
"target": "https://your-oidc-provider.com",
"options": {
"changeOrigin": true
}
},
{
"path": "/api",
"target": "https://your-backend.com",
"options": {
"changeOrigin": true
}
}
]
}Start the server - proxies are automatically configured and all proxy activity is logged!
Use environment variables for flexible configuration:
{
"proxies": [
{
"path": "/api",
"target": "${BACKEND_URL}",
"options": {
"changeOrigin": true
}
}
]
}Set the environment variables in your .env file:
BACKEND_URL=https://api.example.com- Start the server - proxies are automatically configured!
Proxy Logging
All proxy requests and responses are automatically logged to the console:
[Proxy Request] GET /api/users → https://backend.example.com/api/users
[Proxy Request] Authorization: Bearer eyJhbGciOiJIUzI...
[Proxy Response] GET /api/users ← 200 OKThis helps you:
- Debug proxy configuration issues
- Monitor API calls to backend services
- Track authentication headers being forwarded
- Identify failed requests and error codes
Error logging includes detailed information:
[Proxy Error] GET /api/users: ECONNREFUSED
[Proxy Error] Target: https://backend.example.com
[Proxy Error] Code: ECONNREFUSEDExample: Development Setup
For a typical development setup with a separate backend API:
public/proxy.config.json:
{
"proxies": [
{
"path": "/api",
"target": "http://localhost:8080",
"options": {
"changeOrigin": true
}
}
]
}Now all requests to /api/* will be proxied to your backend at http://localhost:8080/api/*.
Rate Limiting
PicoServe includes built-in rate limiting to prevent overwhelming backend services. Configure per-proxy limits:
{
"proxies": [
{
"path": "/api",
"target": "https://your-backend.com",
"options": {
"changeOrigin": true,
"rateLimit": {
"windowMs": 60000,
"max": 100
}
}
}
]
}Options:
windowMs: Time window in milliseconds (default: 60000 = 1 minute)max: Maximum requests per window (default: 100)enabled: Set tofalseto disable for a specific proxy
When you exceed the limit:
{
"error": "Too many requests",
"message": "Please slow down. Maximum 100 requests per 60 seconds."
}This helps you:
- Avoid 429 (Too Many Requests) errors from backends
- Control API usage costs
- Prevent accidental request loops
- Protect backend services from overload
Learn More:
- RATE_LIMITING_GUIDE.md - Complete guide on handling rate limiting
- RATE_LIMITING_ARCHITECTURE.md - How the two-tier rate limiting system works
For more advanced proxy configurations including path rewriting, multiple proxies, and detailed options, see src/api/README.md.
Adding Static Files
Simply place your static files (HTML, CSS, JavaScript, images, etc.) in your configured static directory (defaults to public), and they will be served automatically.
Contributing
Development Setup
If you want to contribute to PicoServe or modify it for your needs:
- Clone the repository:
git clone https://github.com/DenysVuika/PicoServe.git
cd PicoServe- Install dependencies:
npm install- Run in development mode:
npm run dev- Build the project:
npm run build- Test the built version:
npm startProject Structure
PicoServe/
├── src/
│ ├── server.ts # Main server file
│ └── api/ # API plugins directory
│ ├── loader.ts # Plugin loader
│ ├── types.ts # Plugin type definitions
│ ├── proxy.ts # Proxy configuration plugin
│ ├── app.config.ts # App configuration plugin
│ ├── hello.ts # Example plugin
│ ├── example.ts # Example plugin with multiple endpoints
│ └── README.md # API plugin documentation
├── public/ # Static files directory
│ ├── index.html # Sample HTML file
│ ├── jwt-test.html # JWT authentication test UI
│ └── proxy.config.json # Proxy configuration (optional)
├── dist/ # Compiled JavaScript (generated)
├── tsconfig.json # TypeScript configuration
└── package.json # Project dependenciesLicense
ISC
