Andromeda.serve() starts an HTTP/1.1 server that calls your handler for each
incoming request. The handler is the same fetch-style function used by
Cloudflare Workers, Deno, and Bun — it receives a Request and returns a
Response (or Promise<Response>).
The serve extension is included when Andromeda is built with the serve feature
(enabled by default).
Signatures
type ServeHandler = (request: Request) => Response | Promise<Response>;
interface ServeOptions {
port?: number; // default 8080
hostname?: string; // default "127.0.0.1"
signal?: AbortSignal;
reusePort?: boolean;
key?: string; // TLS private key (planned)
cert?: string; // TLS certificate (planned)
onError?: (error: unknown) => Response | Promise<Response>;
onListen?: (info: { hostname: string; port: number }) => void;
handler?: ServeHandler;
}
declare namespace Andromeda {
function serve(handler: ServeHandler): Promise<void>;
function serve(options: ServeOptions): Promise<void>;
function serve(handler: ServeHandler, options: ServeOptions): Promise<void>;
}Hello, world
Andromeda.serve(() => new Response("Hello, Andromeda!"));Visit http://127.0.0.1:8080.
With options
Andromeda.serve({
port: 3000,
hostname: "0.0.0.0",
handler: (req) => new Response(`Hello from ${req.url}`),
});Or pass them as a second argument:
Andromeda.serve(
(req) => new Response(req.method),
{ port: 3000, hostname: "0.0.0.0" },
);Routing
There is no built-in router — use new URL(req.url) and match against
pathname:
Andromeda.serve((req) => {
const url = new URL(req.url);
if (url.pathname === "/health") {
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
}
if (url.pathname === "/echo" && req.method === "POST") {
return req.text().then((body) => new Response(body));
}
if (url.pathname.startsWith("/users/")) {
const id = url.pathname.slice("/users/".length);
return new Response(JSON.stringify({ id }));
}
return new Response("Not Found", { status: 404 });
});Reading the request
Andromeda.serve(async (req) => {
const url = new URL(req.url);
const method = req.method;
const headers = req.headers;
const body = await req.text(); // or .json(), .arrayBuffer()
const ua = headers.get("user-agent");
return new Response(
JSON.stringify({ method, path: url.pathname, ua, body }),
{ headers: { "Content-Type": "application/json" } },
);
});Writing the response
return new Response("hello");
return new Response(JSON.stringify(data), {
status: 201,
headers: {
"Content-Type": "application/json",
"X-Trace-Id": crypto.randomUUID(),
},
});
// Plain HTML
return new Response("<h1>Hi</h1>", {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
// Binary body
const bytes = Andromeda.readFileSync("./logo.png");
return new Response(bytes, {
headers: { "Content-Type": "image/png" },
});Examples
The Andromeda repository contains end-to-end serve examples:
examples/serve/basic.tsexamples/serve/api-server.tsexamples/serve/headers.tsexamples/serve/json-request.tsexamples/serve/query-params.tsexamples/serve/routing.tsexamples/serve/user-agent.ts
Limitations
The current Andromeda.serve is a minimal HTTP/1.1 implementation. The
following features are on the roadmap:
- TLS / HTTPS (
keyandcertoptions) - Connection keep-alive, chunked transfer encoding
- Multipart form handling
- HTTP/2
- WebSocket upgrades
signal/AbortControllerfor graceful shutdownonListencallback invocation
If your workload needs production-grade HTTP, consider running Andromeda behind a reverse proxy such as nginx or Caddy until these gaps close.
See Also
- Fetch API — same
Request/Responsetypes used here - URL API — request parsing
- Streams API — streaming bodies