Redi - Modern Web Server & JavaScript Runtime
Redi is a Go-based web development toolkit that provides a dynamic web server (redi), a Node.js-compatible JavaScript runtime (rejs), and build tools (redi-build) for creating embedded applications and desktop apps.
π Features
Redi Web Server
- Dynamic Routing: Automatic route discovery from filesystem structure with
[param] syntax
- JavaScript API Endpoints: Execute
.js files server-side for API routes
- HTML Template Rendering: Process
.html files with Go templates and server-side JavaScript
- Markdown Support: Automatic
.md to HTML conversion with Goldmark parser
- Template Layouts: Nested layouts with
{{layout 'name'}} syntax
- Background Mode: Run server as daemon with
--log parameter (nohup-like behavior)
- Session-based VM Management: Consistent JavaScript state across requests per client
- Static File Serving: Efficient serving from
public/ directory
- Cross-Platform: Works on Linux, macOS, and Windows
- JavaScript Engine Pooling: High-performance concurrent request handling
Rejs JavaScript Runtime
- Node.js Compatible: Supports CommonJS modules and npm packages
- Built-in Modules:
fs, path, process, child_process, fetch, console, and more
- Event Loop: Full async/await and Promise support
- ES5+ Syntax: Modern JavaScript features via Goja engine
- Module Caching: Efficient require system with path resolution
- Command Line: Run scripts with timeout control
- Cross-Platform: Consistent behavior across all platforms
- Embedded Applications: Create single-file executables with embedded website
- Wails Desktop Apps: Generate cross-platform desktop applications with webview
- Modular Architecture: Extensible builder system for custom deployment types
- Third-party Integration: Clean APIs for building custom applications
π¦ Installation
Download Pre-built Binaries
Download the latest release for your platform from the Releases page. Each archive contains redi, rejs, and redi-build.
# Extract the archive
tar -xzf redi-v1.0.0-linux-amd64.tar.gz
# Move to PATH (extract individual binaries from archive first)
sudo mv redi-v1.0.0-linux-amd64 /usr/local/bin/redi
sudo mv rejs-v1.0.0-linux-amd64 /usr/local/bin/rejs
sudo mv redi-build-v1.0.0-linux-amd64 /usr/local/bin/redi-build
# Verify installation
redi --version
rejs --version
redi-build --version
Build from Source
# Clone the repository
git clone https://github.com/rediwo/redi.git
cd redi
# Build all tools
make build
# Or build individually
go build -o redi ./cmd/redi
go build -o rejs ./cmd/rejs
go build -o redi-build ./cmd/redi-build
π― Quick Start
Redi Web Server
Create a simple website structure:
mysite/
βββ public/
β βββ css/
β β βββ style.css
β βββ js/
β βββ main.js
βββ routes/
βββ index.html
βββ api/
βββ hello.js
routes/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Title}} - My Site</title>
<!-- Tailwind CSS + Vimesh Style -->
<script src="https://unpkg.com/@vimesh/style"></script>
<!-- Alpine.js -->
<script defer src="https://unpkg.com/alpinejs"></script>
<script>
$vs.reset({
aliasColors: {
primary: "#3b82f6"
}
});
</script>
<style>[x-cloak] { display: none !important; }</style>
</head>
<body x-data class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="bg-white rounded-xl shadow-lg p-8">
<h1 class="text-3xl font-bold text-gray-900 mb-4">{{.Title}}</h1>
<p class="text-gray-600 mb-4">Current time: {{.time}}</p>
<p class="text-sm text-gray-500">Request method: {{.method}}</p>
{{if .data}}
<div class="mt-6 p-4 bg-blue-50 rounded-lg">
<h2 class="text-xl font-semibold text-blue-900 mb-2">Submitted Data:</h2>
<pre class="text-sm text-blue-800">{{.data}}</pre>
</div>
{{end}}
</div>
</div>
</body>
</html>
routes/index.js: (corresponding JavaScript file)
// Handle GET requests
exports.get = function(req, res, next) {
res.render({
Title: "Welcome to Redi",
time: new Date().toLocaleTimeString(),
method: req.method
});
};
// Handle POST requests
exports.post = function(req, res, next) {
var body = req.body ? JSON.parse(req.body) : {};
res.render({
Title: "Form Submitted",
data: JSON.stringify(body, null, 2),
time: new Date().toLocaleTimeString(),
method: req.method
});
};
routes/api/hello.js:
// Handle GET requests
exports.get = function(req, res, next) {
res.json({
message: "Hello from Redi API",
timestamp: Date.now(),
method: req.method,
userAgent: req.headers['user-agent']
});
};
// Handle POST requests
exports.post = function(req, res, next) {
var body = req.body ? JSON.parse(req.body) : {};
res.json({
message: "Data received",
receivedData: body,
timestamp: Date.now()
});
};
Run the server:
# Run in foreground
redi --root=mysite --port=8080
# Run in background with logging
redi --root=mysite --port=8080 --log=server.log
Rejs JavaScript Runtime
script.js:
var fs = require('fs');
var path = require('path');
console.log('Running on:', process.platform);
console.log('Script path:', __filename);
// Read a file
var content = fs.readFileSync('data.txt', 'utf8');
console.log('File content:', content);
// Make an HTTP request
var fetch = require('fetch');
fetch('https://api.github.com/users/github')
.then(function(response) {
return response.json();
})
.then(function(data) {
console.log('GitHub user:', data.name);
process.exit(0);
});
Run the script:
# Run with auto-exit for sync scripts
rejs script.js
# Run with timeout for async operations
rejs --timeout=5000 async-script.js
Create embedded applications and desktop apps:
# Create embedded executable
redi-build embed --root=mysite --output=myapp
# Create Wails desktop application
redi-build wails --root=mysite --output=myapp-desktop --name="My Application"
# Show help for specific command
redi-build embed --help
redi-build wails --help
π Documentation
Redi Web Server
CLI Options
--root - Root directory containing public and routes folders (required)
--port - Port to serve on (default: 8080)
--log - Log file path (enables background/daemon mode like nohup)
--version - Show version information
Directory Structure
public/ - Static assets (CSS, JS, images)
routes/ - Dynamic routes and API endpoints
Route Types
.html - HTML templates processed with Go templates
.js - JavaScript files for API endpoints and server-side logic
.md - Markdown files auto-converted to HTML with Goldmark
Dynamic Routes
Use [param] syntax for dynamic segments:
routes/blog/[id].html β /blog/123
routes/users/[name]/profile.html β /users/john/profile
Rejs JavaScript Runtime
CLI Options
--timeout=ms - Set execution timeout in milliseconds
--version - Show version information
CLI Commands
redi-build embed - Create embedded executables:
--root - Root directory to embed (required)
--output - Output executable name (default: "redi-embedded")
redi-build wails - Create Wails desktop applications:
--root - Root directory to embed (required)
--output - Output directory name (default: "redi-app")
--name - Application display name (default: "Redi App")
Global options:
--version - Show version information
--help - Show help information
Built-in Modules
Core Modules:
console - Console output with colors
process - Process information and control
fs - File system operations (sync and async)
path - Path manipulation utilities
child_process - Execute system commands
Network Modules:
fetch - HTTP client with Promise support
Global Objects:
process - Process information and control (Node.js compatible)
__filename - Current script absolute path
__dirname - Current script directory
setTimeout, setInterval, clearTimeout, clearInterval
Module System
// Import built-in module
var fs = require('fs');
// Import local module
var math = require('./lib/math.js');
// Import from node_modules
var lodash = require('lodash');
π οΈ Advanced Features
Building Embedded Applications
Create standalone executables with your website embedded:
# Build embedded executable
redi-build embed --root=mysite --output=myapp
# Run the embedded app
./myapp --port=8080
The embedded executable includes:
- Complete website assets and routes
- Redi server runtime
- Single file deployment
- No external dependencies
Creating Desktop Applications
Generate cross-platform desktop apps using Wails:
# Create Wails desktop application
redi-build wails --root=mysite --output=myapp-desktop --name="My Application"
# Build the desktop app
cd myapp-desktop/my-application
wails build
# Or run in development mode
wails dev
Desktop applications include:
- Embedded web server
- Native window with webview
- Menu system with navigation controls
- Start/stop server controls
- Open in external browser option
Third-Party Integration
Use Redi as a library in your Go applications:
package main
import (
"github.com/rediwo/redi/server"
"github.com/rediwo/redi/runtime"
"github.com/rediwo/redi/builder"
)
func main() {
// Create and start server
config := &server.Config{
Root: "mysite",
Port: 8080,
}
launcher := server.NewLauncher()
launcher.Start(config)
// Execute JavaScript
runtime.ExecuteScript("script.js", []string{}, 0, "1.0.0")
// Build embedded app
embedBuilder := builder.NewEmbedBuilder()
embedBuilder.Build(builder.Config{
Root: "mysite",
Output: "myapp",
})
}
Background Mode & Process Management
Run Redi server in background mode (daemon) with automatic logging:
# Start server in background with logging
redi --root=mysite --port=8080 --log=server.log
# Server runs in background, logs to server.log
# PID saved to server.log.pid for process management
# Stop the background server
kill $(cat server.log.pid)
Session-Based JavaScript State
Redi maintains JavaScript engine state per client session, enabling:
- Persistent variables across requests from the same client
- Session-specific data storage
- Consistent user experience within a session
// routes/api/counter.js
// Each client gets their own counter state
if (!global.counter) {
global.counter = 0;
}
exports.get = function(req, res, next) {
global.counter++;
res.json({
counter: global.counter,
sessionId: req.sessionId // Automatically generated
});
};
Server-Side Rendering with Layouts
routes/_layout/base.html:
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}} - My Site</title>
</head>
<body>
<nav><!-- Navigation --></nav>
{{.Content}}
<footer><!-- Footer --></footer>
</body>
</html>
routes/page.html:
{{layout "base"}}
<script @server>
res.render({
Title: "Page Title"
});
</script>
<h1>Page Content</h1>
HTTP Method Testing
Redi provides comprehensive HTTP method support with built-in testing capabilities:
routes/method-example.js:
exports.get = function(req, res, next) {
res.render({
Title: "HTTP Method Testing",
method: req.method,
message: "GET request received successfully"
});
};
exports.post = function(req, res, next) {
var body = req.body ? JSON.parse(req.body) : {};
res.render({
Title: "HTTP Method Testing",
method: req.method,
message: "POST request received",
receivedData: JSON.stringify(body, null, 2)
});
};
exports.put = function(req, res, next) {
var body = req.body ? JSON.parse(req.body) : {};
res.render({
Title: "HTTP Method Testing",
method: req.method,
message: "PUT request received",
receivedData: JSON.stringify(body, null, 2)
});
};
exports.delete = function(req, res, next) {
res.render({
Title: "HTTP Method Testing",
method: req.method,
message: "DELETE request received"
});
};
Child Process Execution
var child_process = require('child_process');
// Synchronous execution
var output = child_process.execSync('ls -la');
console.log(output.toString());
// Asynchronous execution
child_process.exec('git status', function(error, stdout, stderr) {
if (error) {
console.error('Error:', error);
return;
}
console.log('Output:', stdout);
});
π§ͺ Testing
Run the comprehensive test suite:
# Run all tests (includes integration, API, and module tests)
make test
# Run specific test categories
make test-unit # Unit tests only
make test-integration # Integration tests only
make test-api # API endpoint tests only
# Manual testing with rejs runtime
./rejs fixtures/routes/tests/process_test.js
./rejs --timeout=10000 fixtures/routes/tests/fetch_test.js
# Test build tools
redi-build embed --root=fixtures --output=test-embedded
redi-build wails --help
Test Coverage
Redi includes extensive testing for:
- β
Server Integration: HTTP routing, static files, template rendering
- β
API Endpoints: JavaScript execution, JSON responses, error handling
- β
Dynamic Routing: Parameter extraction, URL patterns
- β
JavaScript Engine: Module system, require paths, session management
- β
Template System: Layout processing, data binding, error handling
- β
Cross-Platform: Windows, Linux, macOS compatibility
- β
Built-in Modules: fs, path, process, fetch, child_process
ποΈ Architecture
Redi is built with a modular architecture for easy extension and third-party integration:
Package Structure
redi/
βββ cmd/ # Command-line tools
β βββ redi/ # Web server CLI
β βββ rejs/ # JavaScript runtime CLI
β βββ redi-build/ # Build tools CLI
βββ server/ # Server management
β βββ config.go # Configuration types
β βββ factory.go # Server factory
β βββ launcher.go # Startup logic
β βββ platform_*.go # Platform-specific code
βββ runtime/ # JavaScript execution
β βββ config.go # Runtime configuration
β βββ executor.go # JavaScript executor
β βββ version.go # Version management
βββ builder/ # Build tools
β βββ types.go # Builder interfaces
β βββ embed.go # Embedded app builder
β βββ wails.go # Wails app builder
βββ handlers/ # Request handlers
βββ modules/ # JavaScript modules
βββ filesystem/ # File system abstractions
βββ fixtures/ # Test website
Design Principles
- Modularity: Each package has a single responsibility
- Extensibility: Clean interfaces for custom builders and handlers
- Third-party Friendly: Simple APIs for integration
- Platform Support: Consistent behavior across operating systems
- No Dependencies: Embedded applications require no external dependencies
Core APIs
Server Management:
// Create and configure server
config := &server.Config{Root: "site", Port: 8080}
launcher := server.NewLauncher()
launcher.Start(config)
JavaScript Execution:
// Execute JavaScript with runtime
config, _ := runtime.NewConfig("script.js")
executor := runtime.NewExecutor()
exitCode, err := executor.Execute(config)
Building Applications:
// Build embedded executable
embedBuilder := builder.NewEmbedBuilder()
err := embedBuilder.Build(builder.Config{
Root: "site", Output: "app"
})
// Build desktop application
wailsBuilder := builder.NewWailsBuilder()
err := wailsBuilder.Build(builder.Config{
Root: "site", Output: "app", AppName: "My App"
})
π€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature)
- Commit your changes (
git commit -m 'Add some amazing feature')
- Push to the branch (
git push origin feature/amazing-feature)
- Open a Pull Request
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π Links
Made with β€οΈ using Go and JavaScript