App
The application root: owns the Server, the route table, the Middleware registry, and the request lifecycle that ties them together.
An App is the only thing that talks to Bun's HTTP server. RouteBase implementations and Middleware instances register themselves against an app, and at App.listen time the app compiles them into a single ServerRouteMap plus a fallback ServerHandler.
const app = new App({ port: 3000, prefix: "/api" });
await app.listen();Contents
App
class
class App implements AppInterfaceAn HTTP application.
The app is a container first: RouteBase implementations and Middleware instances attach to it as they are constructed. Nothing is compiled until App.listen, at which point App.composeRoutes turns routes into a ServerRouteMap and folds the matching Middleware handlers into each route's chain.
Every request follows the same path — build a Context, resolve Context.params, Context.search and Context.body through the parsers registry, run the composeHandlerChain chain, apply CorsInterface, and serialise the Res. Anything thrown along the way is routed to App.handleError.
Constructing an app calls registerApp, so it is discoverable without being passed around.
App.constructor()
constructor(opts?: AppOptions)Creates an app and registers it globally with registerApp.
Parameters
opts—AppOptionsoverriding port, hostname, prefix, idle timeout,TlsOptionsand body size. Any field left out keeps the default declared on the correspondingAppproperty.
App.server
server: Nullable<Server>;The live Server. null until App.listen is called and again after App.close.
App.cors
cors: Optional<CorsInterface>;CorsInterface policy for this app. When set, CorsInterface.handler runs after every handler chain in App.respond and CorsInterface.handlePreflight answers preflight requests. Left unset, App.handlePreflight replies with Status.NO_CONTENT and no CORS headers are added.
App.routes
routes: Array<RouteBase>;RouteBase instances attached to this app, in registration order.
App.middlewares
middlewares: Map<string, Array<Middleware>>;Middleware instances indexed by the RouteBase.id they target. The "*" key holds middlewares that run on every route as well as on the App.handleNotFound path.
App.port
port: number;Port the Server binds to. Defaults to 3000.
App.prefix
prefix: string;Prefix prepended to every RouteBase endpoint on this app. Defaults to "".
App.hostname
hostname: OrString<"0.0.0.0" | "127.0.0.1" | "localhost">;Interface the Server binds to. Defaults to "0.0.0.0".
App.idleTimeout
idleTimeout?: numberSeconds an idle connection is kept open before Bun closes it.
App.tls
tls?: TlsOptionsTlsOptions material. When present the app is served over HTTPS.
App.maxRequestBodySize
maxRequestBodySize?: numberApp-wide body ceiling in bytes passed to Bun. A RouteBase may declare a tighter limit in its Config, enforced per request by enforceBodyLimit.
App.baseUrl
get baseUrl(): stringOrigin the app is reachable at, for example http://0.0.0.0:3000.
Returns — The Server URL once listening; otherwise a URL derived from App.tls, App.hostname and App.port.
App.createServer()
protected createServer(): ServerCompiles App.composeRoutes and App.composeFetch and hands them to Bun.serve, wiring the WebSocket callbacks through to the handlers carried by each WebSocketRoute. Unroutable middlewares are reported first by App.warnUnmatchedMiddlewares.
Calling this when App.server already exists is a no-op that returns the existing one, so it is safe to reach for lazily.
Returns — The running Server.
App.listen()
async listen(): Promise<void>Starts the app.
Installs SIGINT and SIGTERM handlers that call App.close, runs App.handleBeforeListen, then compiles and starts the Server via App.createServer. A failure at any of these steps is logged and the app is closed rather than left half-started.
Returns — A promise that resolves once the Server is listening.
App.close()
async close(closeActiveConnections: boolean = true): Promise<void>Stops the app.
Runs App.handleBeforeClose, stops the Server, and clears App.server. Outside the test value of Config.nodeEnv this also exits the process, so tests can close apps without tearing down the runner.
Parameters
closeActiveConnections— Whether in-flight connections are severed immediately rather than allowed to drain. Defaults totrue.
Returns — A promise that resolves once the Server has stopped.
App.composeRoutes()
protected composeRoutes(): ServerRouteMapCompiles App.routes into the ServerRouteMap Bun expects, keyed by endpoint and then by Method.
Each entry is a full request pipeline wrapped in App.finalize: wildcard segments are lifted into Req.params (Bun does not treat them as params), then params, search and body are parsed and validated against the RouteBase Config — but only the ones the handler chain actually reads, as reported by getContextAccess. Routes with RouteVariant.websocket upgrade the connection into a WebSocketRoute instead of responding, and Method.GET and Method.HEAD skip body work entirely.
Returns — A ServerRouteMap ready to hand to Bun.serve.
Throws — Exception with Status.UPGRADE_REQUIRED when a WebSocket upgrade is rejected.
App.composeFetch()
protected composeFetch(): ServerHandlerBuilds the ServerHandler Bun uses for requests that matched no RouteBase.
Preflight requests — Method.OPTIONS carrying HeaderKey.AccessControlRequestMethod — go to App.handlePreflight. Everything else runs the global ("*") Middleware chain followed by App.handleNotFound, so global middlewares still observe traffic to unknown endpoints.
Returns — The fetch handler for Bun.serve, wrapped by App.finalize.
App.finalize()
protected finalize(handler: ContextHandler): ServerHandlerWraps a ContextHandler into the ServerHandler Bun calls, giving it a Context from App.contextFactory and guaranteeing that every outcome — value or throw — leaves as a Response.
This is the single boundary where errors are caught, so every throw reaches App.handleError through App.respondWithError.
Parameters
handler— The ContextHandler to run for the request.
Returns — A ServerHandler suitable for a ServerRouteMap entry or for Bun.serve's fetch.
App.respond()
protected async respond(context: Context, result: unknown): Promise<Response>Turns a handler's return value into the response sent over the wire.
A returned Res replaces Context.res wholesale; any other defined value becomes Res.body; undefined leaves the existing Res untouched, which is how handlers that mutate Context.res directly are supported. CorsInterface.handler runs last and separately from the Middleware chain, so CORS headers cannot be clobbered by a short-circuiting middleware.
Parameters
context— TheContextfor the request.result— Whatever the handler chain returned.
Returns — The native Response produced by Res.toNativeResponse.
App.respondWithError()
protected async respondWithError(context: Context, err: Error): Promise<Response>Runs App.handleError and responds with its result.
If the error handler itself throws, that second failure is logged and a bare Status.INTERNAL_SERVER_ERROR is returned — the request never escapes without a response.
Parameters
context— TheContextthe failure occurred in.err— TheErrorthrown by the handler chain.
Returns — The error response.
App.handleBeforeListen
handleBeforeListen: Optional<() => MaybePromise<void>>;Hook run inside App.listen, before the Server is created. Use it for setup that must complete before traffic is accepted; throwing here aborts startup and closes the app.
App.handleBeforeClose
handleBeforeClose: Optional<() => MaybePromise<void>>;Hook run inside App.close, before the Server is stopped. Use it to release resources the app owns.
App.handleError
handleError: ErrorHandler;Default ErrorHandler. An Exception is rendered through Exception.toRes; anything else becomes an opaque Status.INTERNAL_SERVER_ERROR Res, so internal failures never leak their message. Replace it to customise error output.
Parameters
err— The thrownError.
Returns — The Res to send.
App.handleNotFound
handleNotFound: ContextHandler;Default ContextHandler for unmatched requests. Replace it to customise the 404 body.
Parameters
c— TheContextfor the unmatched request.
Returns — A Status.NOT_FOUND Res naming the method and URL that did not resolve.
App.handlePreflight
handlePreflight: ContextHandler;Default ContextHandler for CORS preflight requests. Delegates to CorsInterface.handlePreflight when App.cors is configured.
Parameters
c— TheContextfor the preflight request.
Returns — The CORS preflight response, or an empty Status.NO_CONTENT Res when no CorsInterface is set.
App.contextFactory
contextFactory: ContextFactory;Default ContextFactory. Replace it to have the app build a Context subclass carrying your own per-request state.
Parameters
request— The incoming request.server— TheServerthat accepted it.
Returns — A new Context.
App.addMiddleware()
addMiddleware(middleware: Middleware): voidRegisters a Middleware under every RouteBase.id in Middleware.routeIds, so one instance can serve several routes.
Parameters
middleware— TheMiddlewareto register.
App.findMiddlewares()
findMiddlewares(routeId: string): Array<Middleware>Resolves the Middleware instances that apply to a route, global ones first so they wrap the route-specific ones.
Parameters
routeId— The RouteBase.id to resolve for, or"*"to get only the global middlewares without duplicating them.
Returns — The middlewares in execution order.
App.warnUnmatchedMiddlewares()
protected warnUnmatchedMiddlewares(): voidLogs a warning for every Middleware whose target RouteBase.id is not registered on this app and which therefore can never run — usually a typo or a route that was never attached.
Runs from App.createServer rather than App.addMiddleware, because registration order is not guaranteed and a middleware may legally be added before its route.
AppInterface
interface
interface AppInterfaceThe public shape of an application instance, implemented by App.
Depend on this type rather than the App class when you need to accept an app without pinning the implementation.
AppInterface.server
server: Nullable<Server>;The running Server, or null before AppInterface.listen and after AppInterface.close.
AppInterface.cors
cors: Optional<CorsInterface>;CorsInterface policy applied to every response and to preflight requests.
AppInterface.routes
routes: Array<RouteBase>;RouteBase instances registered on this app, in registration order.
AppInterface.middlewares
middlewares: Map<string, Array<Middleware>>;Middleware instances keyed by the RouteBase.id they target; "*" holds the global ones.
AppInterface.port
port: number;TCP port to bind.
AppInterface.prefix
prefix: string;Path prefix prepended to every RouteBase endpoint on this app.
AppInterface.hostname
hostname: OrString<"0.0.0.0" | "127.0.0.1" | "localhost">;Interface to bind.
AppInterface.idleTimeout
idleTimeout?: numberSeconds a connection may stay idle before Bun closes it.
AppInterface.tls
tls?: TlsOptionsTlsOptions material; when set, the app is served over HTTPS.
AppInterface.baseUrl
get baseUrl(): string;Origin the app is reachable at.
AppInterface.listen()
listen(): Promise<void>Compiles RouteBase and Middleware registrations and starts the Server.
AppInterface.close()
close(closeActiveConnections?: boolean): Promise<void>Stops the Server and releases the port.
AppInterface.handleBeforeListen
handleBeforeListen: Optional<() => MaybePromise<void>>;Hook run just before the Server starts.
AppInterface.handleBeforeClose
handleBeforeClose: Optional<() => MaybePromise<void>>;Hook run just before the Server stops.
AppInterface.handleError
handleError: ErrorHandler;ErrorHandler that converts a thrown Error into a response value.
AppInterface.handleNotFound
handleNotFound: ContextHandler;ContextHandler that produces the response for requests matching no RouteBase.
AppInterface.handlePreflight
handlePreflight: ContextHandler;ContextHandler that produces the response for CORS preflight requests.
AppInterface.contextFactory
contextFactory: ContextFactory;ContextFactory that builds the Context for each incoming request.
AppInterface.addMiddleware()
addMiddleware(middleware: Middleware): voidRegisters a Middleware against each RouteBase.id it targets.
AppInterface.findMiddlewares()
findMiddlewares(routeId: string): Array<Middleware>Resolves the Middleware instances that apply to a RouteBase.id.