A modern Express-based API Gateway with dynamic routing, content negotiation, and upstream proxying. Powered by Bun runtime and managed by PM2 process manager.
- β‘ Bun Runtime - Fast JavaScript runtime with built-in bundler, transpiler, and package manager
- π Express Framework - Minimal and flexible Node.js web application framework
- π PM2 Process Manager - Advanced production process manager with load balancing
- π― Dynamic Routing - Data-driven routing based on agent/endpoint configuration
- π Content Negotiation - Serves HTML product pages or JSON data based on Accept headers
- π Upstream Proxy - Transparent proxying to upstream services
- π¨ Beautiful UI - Modern, responsive HTML documentation pages
- π³ Web3 Wallet Support - Phantom wallet integration for x402 payments on Solana
- π Payment Verification - On-chain verification of Solana transactions
- π Security - Helmet.js for security headers
- π CORS - Cross-Origin Resource Sharing enabled
- π Logging - Morgan HTTP request logger
- β»οΈ Hot Reload - Development mode with auto-restart on file changes
- π‘οΈ Error Handling - Centralized error handling middleware
Before you begin, ensure you have installed:
curl -fsSL https://bun.sh/install | bashbun install -g pm2Or using npm:
npm install -g pm2- Clone or navigate to the project directory:
cd /root/x402- Install dependencies:
bun install- Configure environment variables (optional):
cp .env.example .envEdit .env to customize your settings.
Run the server with hot reload:
bun run devStart the application with PM2:
bun run pm2:startOr directly:
pm2 start ecosystem.config.cjsThe following npm scripts are available for PM2 management:
# Start the application
bun run pm2:start
# Stop the application
bun run pm2:stop
# Restart the application
bun run pm2:restart
# Delete from PM2
bun run pm2:delete
# View logs
bun run pm2:logs
# Monitor processes
bun run pm2:monit
# Check status
bun run pm2:status# Start with environment
pm2 start ecosystem.config.cjs --env production
# View logs
pm2 logs express-bun-server
# Monitor CPU/Memory
pm2 monit
# List all processes
pm2 list
# Stop all processes
pm2 stop all
# Restart all processes
pm2 restart all
# Delete all processes
pm2 delete all
# Save PM2 process list (persist across reboots)
pm2 save
# Startup script (auto-start on system boot)
pm2 startupThe gateway uses a data-driven architecture where agents expose multiple endpoints. Each endpoint can:
- Serve HTML product description pages (for browsers/documentation)
- Proxy JSON data from upstream services (for API clients)
The server uses HTTP Accept headers to determine response format:
Accept: text/htmlβ Beautiful HTML product pageAccept: application/jsonβ Proxied JSON from upstream
GET /healthReturns server health status, uptime, runtime information, and agent/endpoint counts.
GET /Returns gateway information. HTML view shows hero page, JSON shows system info.
GET /agentsReturns all available agents. HTML view shows agent grid, JSON shows agent data.
GET /agents/{agentId}Returns specific agent and its endpoints. HTML view shows endpoint cards, JSON shows structured data.
All endpoints defined in agents.js are automatically routed:
Current Weather (HTML Documentation)
curl -H "Accept: text/html" http://localhost:3000/weather/currentCurrent Weather (JSON Data)
curl -H "Accept: application/json" http://localhost:3000/weather/currentWeather Forecast
curl -H "Accept: application/json" http://localhost:3000/weather/forecastData Analysis
curl -H "Accept: application/json" http://localhost:3000/data/analyzeData Transformation (POST)
curl -X POST http://localhost:3000/data/transform \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"format":"json","data":{}}'Text Summarization
curl -X POST http://localhost:3000/ai/summarize \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"text":"Your text here","length":"medium"}'Sentiment Analysis
curl -X POST http://localhost:3000/ai/sentiment \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"text":"I love this product!"}'Simply visit any endpoint in your browser to see the HTML documentation:
- http://localhost:3000/
- http://localhost:3000/agents
- http://localhost:3000/weather/current
- http://localhost:3000/data/analyze
Use curl with JSON accept header:
# Health check
curl -H "Accept: application/json" http://localhost:3000/health
# List agents
curl -H "Accept: application/json" http://localhost:3000/agents
# Get specific agent
curl -H "Accept: application/json" http://localhost:3000/agents/weather-agent
# Call endpoint (proxied to upstream)
curl -H "Accept: application/json" http://localhost:3000/weather/currentEdit .env file to configure:
NODE_ENV- Environment mode (development/production)PORT- Server port (default: 3000)HOST- Server host (default: 0.0.0.0)PUBLIC_URL- Public URL for API (used in HTML docs and examples)- Development:
http://localhost:3000 - Production:
https://api.yourdomain.com - Default:
http://{HOST}:{PORT} - Note: Trailing slashes are automatically stripped for clean URLs
- Development:
Edit ecosystem.config.cjs to customize:
instances- Number of instances to runmax_memory_restart- Auto-restart if memory exceeds limitexec_mode- Execution mode (cluster/fork)watch- Enable file watchingcron_restart- Schedule automatic restartsenv- Environment variables for different modes
Use a baseUrl for agents with multiple endpoints sharing the same API base:
{
id: 'my-agent',
name: 'My Custom Agent',
description: 'Description of what this agent does',
icon: 'π―',
baseUrl: 'https://api.example.com/v1', // Base URL for all endpoints
endpoints: [
{
id: 'my-endpoint',
name: 'My Endpoint',
description: 'What this endpoint does',
path: '/my-agent/endpoint',
upstreamUrl: '/data', // Relative to baseUrl
method: 'GET',
parameters: 'param1=value',
exampleResponse: { result: 'example' }
},
{
id: 'another-endpoint',
name: 'Another Endpoint',
description: 'Another endpoint',
path: '/my-agent/other',
upstreamUrl: '/other', // Also relative to baseUrl
method: 'POST',
parameters: '', // No parameters
exampleResponse: { success: true }
}
]
}Result:
/my-agent/endpointβhttps://api.example.com/v1/data/my-agent/otherβhttps://api.example.com/v1/other
The upstreamUrl can be:
- Relative path (combined with agent's
baseUrl):/path - Full URL (overrides agent's
baseUrl):https://different-api.com/endpoint
See AGENT_STRUCTURE.md for detailed documentation.
Endpoints automatically:
- Appear in the agents list and UI
- Serve HTML documentation at the endpoint path
- Proxy JSON requests to the upstream URL
- Validate HTTP method
- Combine agent baseUrl with endpoint path
x402/
βββ index.js # Main Express application with routing logic
βββ agents.js # Agent and endpoint configuration (data structure)
βββ templates.js # HTML template generators for product pages
βββ package.json # Project dependencies and scripts
βββ ecosystem.config.cjs # PM2 configuration
βββ .env # Environment variables (not in git)
βββ env.example # Example environment variables template
βββ .gitignore # Git ignore patterns
βββ README.md # This file (main documentation)
βββ QUICKSTART.md # Quick start guide
βββ EXAMPLES.md # Usage examples
βββ AGENT_STRUCTURE.md # Agent configuration guide
βββ GROUPS_GUIDE.md # Groups and multiple baseURLs guide
βββ logs/ # PM2 logs directory (auto-created)
- index.js - Express server with content negotiation and dynamic routing
- agents.js - Data-driven configuration for all agents and endpoints (Edit this to add endpoints!)
- templates.js - HTML page generators for beautiful documentation
- public/wallet-connector.js - Client-side wallet connector for Phantom integration (for x402-enabled upstreams)
- ecosystem.config.cjs - PM2 process manager configuration
- package.json - Dependencies and npm scripts
- AGENT_STRUCTURE.md - Detailed guide for configuring agents with baseUrl
PM2 logs are stored in the logs/ directory:
error.log- Error logsout.log- Standard output logscombined.log- Combined logs
View logs in real-time:
pm2 logs express-bun-serverThe gateway provides a client-side wallet connector to help users interact with x402-enabled upstream services.
- Upstream Services: Your upstream APIs may implement the x402 payment protocol
- Client Wallet: Users can connect their Phantom wallet via the gateway's UI
- Payment Flow:
- User requests an endpoint
- If upstream returns 402 Payment Required, payment details are shown
- User pays the upstream service directly using their wallet
- Gateway forwards the request with payment proof headers
- Upstream validates payment and returns data
-
Install Phantom Wallet (for users):
- Visit https://phantom.app/
- Install browser extension
- Create or import a wallet
-
Get Test SOL (for devnet testing):
- Visit https://solfaucet.com/
- Request devnet SOL
- Or use:
solana airdrop 1 YOUR_ADDRESS --url devnet
-
Use the UI:
- Navigate to any endpoint page in your browser
- Connect Phantom Wallet if the upstream requires payment
- Pay the upstream service to access protected endpoints
- π³ Phantom Wallet Support - Seamless browser wallet integration
- βοΈ Multi-Network - Support for mainnet, devnet, and testnet
- π Cryptographic Proofs - Sign and verify payment messages
- π‘ x402 Headers - Automatic payment header management
- π Transparent Proxy - Gateway forwards payment headers to upstream services
The wallet connector is purely client-side and helps users make payments to x402-enabled upstream services. The gateway acts as a transparent proxy, forwarding payment headers from the client to the upstream for verification.
Note: Payment verification is handled by the upstream services, not by this gateway.
Bun provides excellent performance benefits:
- Fast startup - 3-4x faster than Node.js
- Low memory usage - More efficient memory management
- Built-in utilities - No need for additional tools like nodemon
If port 3000 is already in use, change the PORT in .env file or stop the conflicting process:
# Find process using port 3000
lsof -i :3000
# Kill the process
kill -9 <PID>Ensure PM2 is installed globally:
bun install -g pm2
# or
npm install -g pm2Install Bun runtime:
curl -fsSL https://bun.sh/install | bashMIT
Built with β€οΈ using Bun, Express, and PM2