Development server functionality providing hot module reloading, WebSocket connections, middleware support, and on-demand bundling for fast development cycles. Metro's development server enables rapid iteration with sub-second reload times.
Starts an HTTP/HTTPS development server with Metro middleware for serving bundles and assets.
/**
* Start HTTP/HTTPS development server with Metro middleware
* @param config - Metro configuration object
* @param options - Server options and callbacks
* @returns Promise resolving to server instance
*/
function runServer(
config: ConfigT,
options?: RunServerOptions
): Promise<RunServerResult>;
interface RunServerOptions {
/** Indicate reduced performance mode */
hasReducedPerformance?: boolean;
/** Host to bind server to (default: localhost) */
host?: string;
/** Error callback for server errors */
onError?: (error: Error & {code?: string}) => void;
/** Callback when server is ready */
onReady?: (server: HttpServer | HttpsServer) => void;
/** Callback when server closes */
onClose?: () => void;
/** HTTPS server options */
secureServerOptions?: Record<string, unknown>;
/** Enable HTTPS (deprecated, use secureServerOptions) */
secure?: boolean;
/** HTTPS certificate path (deprecated) */
secureCert?: string;
/** HTTPS private key path (deprecated) */
secureKey?: string;
/** Additional middleware handlers */
unstable_extraMiddleware?: ReadonlyArray<HandleFunction>;
/** Wait for bundler initialization */
waitForBundler?: boolean;
/** Enable file watching */
watch?: boolean;
/** Custom WebSocket endpoints */
websocketEndpoints?: Record<string, WebsocketServer>;
}
interface RunServerResult {
/** HTTP or HTTPS server instance */
httpServer: HttpServer | HttpsServer;
}Usage Example:
import { runServer, loadConfig } from "metro";
import https from "https";
import fs from "fs";
const config = await loadConfig({
projectRoot: process.cwd(),
});
// Start HTTPS development server
const { httpServer } = await runServer(config, {
host: "0.0.0.0",
secureServerOptions: {
key: fs.readFileSync("./certs/key.pem"),
cert: fs.readFileSync("./certs/cert.pem"),
},
onReady: (server) => {
const address = server.address();
console.log(`Metro server running on https://${address.address}:${address.port}`);
},
onError: (error) => {
console.error("Server error:", error.message);
},
waitForBundler: true,
watch: true,
});
// Handle graceful shutdown
process.on("SIGTERM", () => {
httpServer.close();
});Creates Connect-compatible middleware for integration with existing Express or Connect applications.
/**
* Create Connect-compatible middleware for custom servers
* @param config - Metro configuration object
* @param options - Middleware creation options
* @returns Promise resolving to Metro middleware object
*/
function createConnectMiddleware(
config: ConfigT,
options?: RunMetroOptions
): Promise<MetroMiddleWare>;
interface MetroMiddleWare {
/** Attach HMR server to existing HTTP server */
attachHmrServer: (httpServer: HttpServer | HttpsServer) => void;
/** Cleanup middleware and server */
end: () => Promise<void>;
/** Metro server instance */
metroServer: MetroServer;
/** Connect middleware function */
middleware: Middleware;
}
interface RunMetroOptions extends ServerOptions {
/** Wait for bundler initialization before resolving */
waitForBundler?: boolean;
}Usage Example:
import express from "express";
import { createConnectMiddleware, loadConfig } from "metro";
const config = await loadConfig({
projectRoot: process.cwd(),
});
// Create Express app with Metro middleware
const app = express();
const server = http.createServer(app);
// Add Metro middleware
const {
middleware,
attachHmrServer,
end: endMiddleware,
} = await createConnectMiddleware(config, {
waitForBundler: true,
});
app.use("/api", (req, res) => {
res.json({ status: "ok" });
});
// Add Metro middleware after custom routes
app.use(middleware);
// Attach HMR server for hot reloading
attachHmrServer(server);
server.listen(3000, () => {
console.log("Server with Metro running on http://localhost:3000");
});
// Handle shutdown
process.on("SIGTERM", async () => {
await endMiddleware();
server.close();
});Metro provides built-in hot module reloading through WebSocket connections for rapid development feedback.
/**
* HMR Server for managing hot module reloading connections
*/
class HmrServer {
constructor(
bundler: IncrementalBundler,
createModuleId: (path: string) => number,
config: ConfigT
);
/** Handle client connection for HMR */
onClientConnect(
requestUrl: string,
sendFn: (data: string) => void
): Promise<Client>;
/** Handle client disconnection */
onClientDisconnect(client: Client): void;
/** Handle client messages */
onClientMessage(client: Client, message: string): void;
}
interface WebsocketServer extends EventEmitter {
handleUpgrade<T = WebsocketServer>(
request: IncomingMessage,
socket: Duplex,
upgradeHead: Buffer,
callback: (client: T, request: IncomingMessage) => void
): void;
emit(event: 'connection', ws: T, request: IncomingMessage): boolean;
}
interface Client {
id: string;
send: (data: string) => void;
close: () => void;
}Usage Example:
// HMR is automatically enabled when using runServer or createConnectMiddleware
// Client-side code connects to WebSocket endpoint at /hot
// Custom WebSocket endpoints can be added:
const { httpServer } = await runServer(config, {
websocketEndpoints: {
"/custom": customWebSocketServer,
},
});Metro provides built-in reporting for development feedback and build progress.
/**
* Terminal-based reporter for development console output
*/
class TerminalReporter {
constructor(terminal: Terminal);
/** Update reporter with build events */
update(event: TerminalReportableEvent): void;
}
/**
* JSON-based reporter for structured logging
*/
class JsonReporter {
constructor(stream: Writable);
/** Update reporter with build events */
update(event: ReportableEvent): void;
}
/**
* Terminal utility for reporter output
*/
class Terminal {
constructor(stream: Writable);
}interface ServerConfig {
/** Server port (default: 8081) */
port: number;
/** Enable HTTPS */
useGlobalHotkey: boolean;
/** Middleware enhancement function */
enhanceMiddleware?: (
middleware: Middleware,
server: MetroServer
) => Middleware;
/** Verify requests before processing */
verifyConnections: boolean;
}
interface ServerOptions {
/** Indicate reduced performance mode */
hasReducedPerformance?: boolean;
/** Enable file watching */
watch?: boolean;
}
type Middleware = (
req: IncomingMessage,
res: ServerResponse,
next: (error?: Error) => void
) => void;
type HandleFunction = (
req: IncomingMessage,
res: ServerResponse,
next: (error?: Error) => void
) => void;Development server includes comprehensive error handling for common development scenarios:
// Server startup errors
const { httpServer } = await runServer(config, {
onError: (error) => {
if (error.code === "EADDRINUSE") {
console.error("Port already in use");
} else if (error.code === "EACCES") {
console.error("Permission denied");
} else {
console.error("Server error:", error.message);
}
},
});
// Middleware errors are handled automatically and reported through the reporter system