HTTP Server
Frame-Master wraps Bun.serve() with a plugin-aware request pipeline.
Overview
Built on Bun: https://bun.sh/docs/api/http
Server Initialization
Startup steps:
- Load
frame-master.config.ts - Initialize plugins in order
- Merge
serverConfigfrom plugins withHTTPServer - Run
serverStarthooks - (Dev) attach file watchers
- Start
Bun.serve()with merged config and fetch handler
export default async (params?: {
config?: FrameMasterConfig;
pluginLoader?: PluginLoader;
builder?: Builder;
}) => {
// Loads and initializes all core components of the server.
await InitAll({ loders: params });
Bun.serve({...});
};Warning: Conflicting server options (e.g., ports) throw unless
disableHttpServerOptionsConflictWarningis true.
Route Handling
Static Routes
const config: FrameMasterConfig = {
HTTPServer: {
port: 3000,
routes: {
"/api/health": () => Response.json({ status: "ok" }),
"/api/version": () =>
Response.json({ version: "1.0.0", framework: "Frame-Master" }),
},
},
};Plugin Routes
const myPlugin: FrameMasterPlugin = {
name: "my-api-plugin",
version: "1.0.0",
serverConfig: {
routes: {
"/api/custom": () => Response.json({ message: "From plugin" }),
},
},
router: {
request(master) {
if (master.pathname === "/api/complex") {
master.setResponse(JSON.stringify({ data: "Complex logic" }), {
headers: { "Content-Type": "application/json" },
});
}
},
},
};Route priority:
- Plugin routes (earlier plugins first)
- HTTPServer config routes
masterRequestfallback via plugin request hooks
WebSocket Support
const config: FrameMasterConfig = {
HTTPServer: {
port: 3000,
websocket: {
maxPayloadLength: 16 * 1024 * 1024,
idleTimeout: 120,
backpressureLimit: 1024 * 1024,
perMessageDeflate: true,
},
},
};Plugin upgrade + handlers:
const wsPlugin: FrameMasterPlugin = {
name: "websocket-plugin",
version: "1.0.0",
serverConfig: {
routes: {
"/ws/my-identifier": (req, server) =>
server.upgrade(req as Request, { data: { id: "my-identifier" } }) ||
Response.json({ error: "WebSocket upgrade failed" }, { status: 400 }),
},
},
websocket: {
onOpen(ws) {
const { id } = ws.data as { id: string };
ws.send(
id === "my-identifier"
? "Welcome, my-identifier!"
: "Welcome, unknown identifier!",
);
},
onMessage(ws, message) {
const { id } = ws.data as { id: string };
if (id === "my-identifier") ws.send(`Echo: ${message}`);
},
onClose(ws) {
const { id } = ws.data as { id: string };
if (id === "my-identifier") console.log("WebSocket closed");
},
},
};More: https://bun.sh/docs/api/websockets
Development Features
File System Watchers
absolutePathrefers to the full path from the root of the project, whilefilePathis relative to the watched directory.
const myPlugin: FrameMasterPlugin = {
name: "my-plugin",
version: "1.0.0",
fileSystemWatchDir: ["src/pages", "src/components"],
onFileSystemChange(event, file, absolutePath) {
console.log(`${event}: ${file}`);
if (event === "change" && file.endsWith(".tsx"))
rebuildComponent(absolutePath);
},
};Production: watchers are disabled when
NODE_ENV=production.
Best Practices
- Set
development: trueduring local dev for better errors - Keep plugin request hooks fast; avoid heavy sync work
- Place request-modifying plugins early, response-modifying plugins later
- Use custom error handlers for better UX and security
- Disable noisy logging in production
External Resources
- Bun HTTP: https://bun.sh/docs/api/http
- Bun WebSocket: https://bun.sh/docs/api/websockets
- Bun routing: https://bun.sh/docs/api/http#routing
- MDN Fetch API: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
