A flexible Express middleware for reverse proxying requests between Node.js and Go services, designed to facilitate gradual migration from Node.js to Go applications.
- 🔄 Seamless Request Forwarding: Proxy HTTP requests with support for all methods (GET, POST, PUT, DELETE, etc.)
- 📁 File Upload Support: Handle multipart/form-data with file uploads using multer
- 📊 Request/Response Callbacks: Access request and response data for logging, analytics, or custom processing
- 🚀 Streaming Support: Efficient streaming for large responses when no callback is needed
- ⚡ Zero-Downtime Migration: Gradually migrate routes from Node.js to Go without service interruption
- 🛡️ Error Handling: Robust error handling with proper HTTP status codes
- 🔧 Configurable: Environment-based configuration with sensible defaults
npm installimport express from 'express';
import { expressMiddlewareProxy } from './express-middleware';
const app = express();
// Proxy all requests to Go service
app.use('/api', expressMiddlewareProxy({
host: 'localhost',
port: 8080
}));
app.listen(3000);// Set environment variables
process.env.GO_HOST = 'localhost';
process.env.GO_PORT = '8080';
// Use defaults from environment
app.use('/api', expressMiddlewareProxy());app.use('/api/users', expressMiddlewareProxy(
{ host: 'localhost', port: 8080 },
async (req, res, responseData) => {
// Log request/response for analytics
console.log(`${req.method} ${req.url} - ${res.statusCode}`);
console.log('Response size:', responseData.length);
// Save to analytics database
await analytics.track({
method: req.method,
url: req.url,
statusCode: res.statusCode,
responseSize: responseData.length
});
}
));import multer from 'multer';
const upload = multer({ dest: 'uploads/' });
app.post('/upload',
upload.fields([{ name: 'files', maxCount: 10 }]),
expressMiddlewareProxy({
host: 'localhost',
port: 8080,
path: '/upload'
})
);| Parameter | Type | Default | Description |
|---|---|---|---|
host |
string |
process.env.GO_HOST or 'localhost' |
Target server hostname |
port |
number |
process.env.GO_PORT or 80 |
Target server port |
path |
string |
req.url |
Target path (defaults to original request path) |
method |
string |
req.method |
HTTP method (defaults to original request method) |
headers |
object |
{} |
Additional headers to add to the proxied request |
The optional callback function receives three parameters:
callback(req: CustomRequest, res: Response, responseData: string) => void | Promise<void>req- Original Express request objectres- Express response objectresponseData- Complete response data from the target server
Note: The callback is for computation only. It doesn't write to the response stream.
Perfect for migrating from Node.js to Go services incrementally:
// Migrate user service first
app.use('/api/users', expressMiddlewareProxy({ port: 8080 }));
// Keep other services in Node.js for now
app.use('/api/auth', authRoutes);
app.use('/api/orders', orderRoutes);Route traffic to different service versions:
app.use('/api/v2', expressMiddlewareProxy({
port: process.env.NEW_SERVICE_PORT
}));Route to different services based on path:
app.use('/api/users', expressMiddlewareProxy({ port: 8001 }));
app.use('/api/products', expressMiddlewareProxy({ port: 8002 }));
app.use('/api/orders', expressMiddlewareProxy({ port: 8003 }));Monitor and analyze traffic:
app.use('/api', expressMiddlewareProxy(
{ port: 8080 },
async (req, res, data) => {
await logger.info({
timestamp: new Date(),
method: req.method,
url: req.url,
statusCode: res.statusCode,
responseSize: data.length,
userAgent: req.headers['user-agent']
});
}
));Here's a simple Go server that works with this proxy:
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", handleRequest)
fmt.Println("Go server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
// Read request body
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Echo back the request info
response := map[string]interface{}{
"method": r.Method,
"path": r.URL.Path,
"body": string(body),
"headers": r.Header,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}| Variable | Description | Default |
|---|---|---|
GO_HOST |
Target Go server hostname | localhost |
GO_PORT |
Target Go server port | 80 |
The middleware handles various error scenarios:
- Connection errors: Returns 502 Bad Gateway
- Timeout errors: Returns 504 Gateway Timeout
- Invalid responses: Returns 500 Internal Server Error
- 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
This project is licensed under the MIT License - see the LICENSE file for details.
- Built for gradual Node.js to Go migration scenarios
- Inspired by the need for zero-downtime service transitions
- Designed with production reliability in mind