Custom Server
Rex can be embedded into your own server, giving you full control over the HTTP layer while Rex handles routing, bundling, and SSR.
Node.js / Bun (NAPI)
Rex provides NAPI bindings so you can use it from Node.js or Bun. The request handler uses the Web Fetch API (Request/Response):
import { createRex } from "@limlabs/rex/server";
const rex = await createRex({ root: import.meta.dirname });
const handle = rex.getRequestHandler();
const server = Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url);
// Add your own routes or middleware here
if (url.pathname === "/healthz") {
return new Response("ok");
}
// Let Rex handle everything else
return handle(req);
},
});
console.log(`Custom server listening on http://localhost:${server.port}`);The getRequestHandler() returns a function with signature (req: Request) => Promise<Response>, using the standard Web Fetch API. This works natively with Bun and can be adapted for Node.js using a Fetch-compatible server.
Rust
Embed Rex directly in your own Axum server for maximum performance:
use axum::{Router, routing::get};
use rex_server::{Rex, RexOptions};
#[tokio::main]
async fn main() {
let rex = Rex::new(RexOptions {
root: "./my-app".into(),
..Default::default()
}).await.unwrap();
let app = Router::new()
.route("/healthz", get(|| async { "ok" }))
.merge(rex.router());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}When to use a custom server
- Adding custom API endpoints alongside Rex pages
- Integrating with existing services or middleware
- Custom authentication or rate limiting at the HTTP layer
- Health checks and observability endpoints for your infrastructure
For most applications, rex start is sufficient. Use a custom server only when you need control that Rex's built-in server doesn't provide.