Get Started
There are two ways of using this reverse proxy: as a library or as a CLI.
Library
Given the npm package is installed:
import type { TlsConfig } from '@stacksjs/rpx'
import { startProxy } from '@stacksjs/rpx'
export interface CleanupConfig {
hosts: boolean // clean up /etc/hosts, defaults to false
certs: boolean // clean up certificates, defaults to false
}
export interface ReverseProxyConfig {
from: string // domain to proxy from, defaults to localhost:3000
to: string // domain to proxy to, defaults to stacks.localhost
cleanUrls?: boolean // removes the .html extension from URLs, defaults to false
https: boolean | TlsConfig // automatically uses https, defaults to true, also redirects http to https
cleanup?: boolean | CleanupConfig // automatically cleans up /etc/hosts, defaults to false
verbose: boolean // log verbose output, defaults to false
}
const config: ReverseProxyOptions = {
from: 'localhost:3000',
to: 'my-docs.localhost',
cleanUrls: true,
https: true,
cleanup: false,
}
startProxy(config)
In case you are trying to start multiple proxies, you may use this configuration:
// reverse-proxy.config.{ts,js}
import type { ReverseProxyOptions } from '@stacksjs/rpx'
import os from 'node:os'
import path from 'node:path'
const config: ReverseProxyOptions = {
https: { // https: true -> also works with sensible defaults
caCertPath: path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.ca.crt`),
certPath: path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.crt`),
keyPath: path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.crt.key`),
},
cleanup: {
hosts: true,
certs: false,
},
proxies: [
{
from: 'localhost:5173',
to: 'my-app.localhost',
cleanUrls: true,
},
{
from: 'localhost:5174',
to: 'my-api.local',
},
],
verbose: true,
}
export default config
CLI
rpx --from localhost:3000 --to my-project.localhost
rpx --from localhost:8080 --to my-project.test --keyPath ./key.pem --certPath ./cert.pem
rpx --help
rpx --version
Testing
bun test
Usage
Learn how to use TypeScript Rate Limiter in your application.
Basic Usage
The simplest way to use the rate limiter is with the default memory storage and fixed window algorithm:
import { RateLimiter } from 'ts-rate-limiter'
// Create a rate limiter with 100 requests per minute
const limiter = new RateLimiter({
windowMs: 60 * 1000, // 1 minute
maxRequests: 100 // limit each IP to 100 requests per windowMs
})
// In your Bun server
const server = Bun.serve({
port: 3000,
async fetch(req) {
// Use as middleware
const limiterResponse = await limiter.middleware()(req)
if (limiterResponse) {
return limiterResponse // Returns 429 Too Many Requests if rate limit exceeded
}
// Continue with your normal request handling
return new Response('Hello World')
}
})
Checking Rate Limits
You can manually check if a request is rate limited without immediately returning a response:
import { RateLimiter } from 'ts-rate-limiter'
const limiter = new RateLimiter({
windowMs: 15 * 60 * 1000, // 15 minutes
maxRequests: 100,
})
async function handleRequest(req: Request) {
const result = await limiter.check(req)
if (!result.allowed) {
console.log(`Rate limit exceeded: ${result.current}/${result.limit}`)
console.log(`Reset in ${Math.ceil(result.remaining / 1000)} seconds`)
// Handle rate limiting in your own way
return new Response('Too many requests', { status: 429 })
}
// Process the request normally
return new Response('Request processed')
}
Using Different Algorithms
You can choose between three different rate limiting algorithms:
// Fixed Window (default)
const fixedWindowLimiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 100,
algorithm: 'fixed-window',
})
// Sliding Window (more accurate)
const slidingWindowLimiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 100,
algorithm: 'sliding-window',
})
// Token Bucket (allows bursts)
const tokenBucketLimiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 100,
algorithm: 'token-bucket',
})
Using Redis Storage
For distributed applications running on multiple servers, use Redis storage:
import { createClient } from 'redis'
import { RateLimiter, RedisStorage } from 'ts-rate-limiter'
// Create Redis client
const redisClient = createClient({
url: 'redis://redis-server:6379'
})
await redisClient.connect()
// Create Redis storage
const storage = new RedisStorage({
client: redisClient,
keyPrefix: 'ratelimit:',
enableSlidingWindow: true
})
// Create rate limiter with Redis storage
const limiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 100,
storage,
algorithm: 'sliding-window'
})
Custom Key Generation
You can customize how keys are generated, for example, to limit by user ID instead of IP:
const userLimiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 50,
keyGenerator: (req) => {
const userId = getUserIdFromRequest(req)
return userId || req.headers.get('x-forwarded-for') || '127.0.0.1'
}
})
Skipping Certain Requests
You can skip rate limiting for certain requests:
const limiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 100,
skip: (req) => {
// Skip rate limiting for health checks and static assets
const url = new URL(req.url)
return url.pathname === '/health' || url.pathname.startsWith('/static/')
}
})
Custom Response Handling
You can customize the response when rate limiting is triggered:
const limiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 100,
handler: (req, result) => {
return new Response(JSON.stringify({
error: 'Too many requests',
retryAfter: Math.ceil(result.remaining / 1000),
}), {
status: 429,
headers: {
'Content-Type': 'application/json',
'Retry-After': Math.ceil(result.remaining / 1000).toString(),
}
})
}
})
Draft Mode
During testing or development, you might want to monitor rate limiting without actually blocking requests:
const limiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 100,
draftMode: process.env.NODE_ENV !== 'production',
})