Portable wildcard subdomain routing with pluggable storage backends
npm install @just-be/wildcardPortable wildcard subdomain routing library with pluggable storage backends. Route subdomains to static files, redirects, or proxied URLs with dependency injection for maximum flexibility.
- Pluggable storage backends - Use any file storage (R2, S3, filesystem, etc.)
- Security built-in - SSRF protection, path traversal prevention, safe header filtering
- Three routing modes:
- Static - Serve files from any storage backend with SPA mode and custom 404s
- Redirect - 301/302 redirects to external URLs
- Rewrite - Proxy requests to external services
- Framework-agnostic - Works with Cloudflare Workers, Node.js, Deno, Bun, etc.
- Type-safe - Full TypeScript support with Zod validation
``bash`
bun add @just-be/wildcard zod
`ts
import type { FileLoader, RouteConfigLoader } from "@just-be/wildcard";
// Implement your file loader (example with filesystem)
const fileLoader: FileLoader = {
async loadFile(path: string) {
try {
const content = await fs.readFile(path);
return {
body: content,
etag: generateEtag(content),
headers: new Headers(),
};
} catch {
return null;
}
},
};
// Implement your route config loader (example with JSON file)
const routeConfigLoader: RouteConfigLoader = {
async loadRouteConfig(subdomain: string) {
try {
const config = await fs.readFile(./config/${subdomain}.json, "utf-8");`
return config;
} catch {
return null;
}
},
};
`ts
import { handleStatic, handleRedirect, handleRewrite, StaticConfigSchema } from "@just-be/wildcard";
// Load and validate config
const configJson = await routeConfigLoader.loadRouteConfig("myapp");
const config = StaticConfigSchema.parse(JSON.parse(configJson));
// Handle the request - pass dependencies after config
const response = await handleStatic(request, config, fileLoader);
// Handlers without dependencies don't need extra args
const redirectResponse = await handleRedirect(request, redirectConfig);
`
`ts
import { StaticConfigSchema } from "@just-be/wildcard";
const config = {
type: "static",
path: "apps/myapp", // Base path in storage
spa: true, // Optional: SPA mode (all routes serve index.html)
fallback: "404.html", // Optional: Custom 404 (only in non-SPA mode)
};
`
`ts
import { RedirectConfigSchema } from "@just-be/wildcard";
const config = {
type: "redirect",
url: "https://example.com",
permanent: false, // Optional: 301 vs 302
};
`
`ts
import { RewriteConfigSchema } from "@just-be/wildcard";
const config = {
type: "rewrite",
url: "https://api.example.com",
allowedMethods: ["GET", "POST"], // Optional: defaults to ["GET", "HEAD", "OPTIONS"]
};
`
The library uses dependency injection to decouple from specific storage backends:
`ts
interface FileLoader {
loadFile(path: string): Promise
}
interface RouteConfigLoader {
loadRouteConfig(subdomain: string): Promise
}
interface FileObject {
body: ReadableStream
headers?: Headers;
etag?: string;
size?: number;
}
`
- SSRF Protection - Blocks requests to private IPs and localhost
- Path Traversal Prevention - Sanitizes all file paths
- Header Filtering - Only forwards safe headers in proxy mode
- Content Type Detection - Sets correct MIME types
- Security Headers - Adds CSP, X-Frame-Options, etc.
See the services/wildcard directory for a complete Cloudflare Workers implementation using R2 and KV.
- handleStatic(request, config, fileLoader) - Serve static files from storagehandleRedirect(request, config)
- - Handle URL redirects (301/302)handleRewrite(request, config)
- - Proxy requests to external services
- StaticConfigSchema - Validates static file configRedirectConfigSchema
- - Validates redirect configRewriteConfigSchema
- - Validates rewrite configRouteConfigSchema
- - Discriminated union of all configs
- sanitizePath(path) - Sanitize file pathsisSafeURL(url)
- - Validate URLs for SSRFgetContentType(path)
- - Detect MIME typesfilterSafeHeaders(headers)
- - Filter request headersisValidSubdomain(subdomain)` - Validate subdomain format
-
MIT