Getting Started

Build HTTP servers with web standard APIs like fetch, Request, and Response.

Quick Start (CLI)

Create a server entry:

server.ts
export default {
  fetch(req: Request) {
    return Response.json({ hello: "world!" });
  },
};

Then, run the server using your favorite runtime:

npx srvx
Read more in Using CLI.
You can also try examples in the online playground

Quick Start (API)

Instead of using the srvx CLI, you can directly import the serve method to define a self-listening server entry.

Create a server entry:

server.ts
import { serve } from "srvx";

const server = serve({
  fetch(request) {
    return Response.json({ hello: "world!" });
  },
});

Install srvx as a dependency:

npm i srvx

Then, run the server using your favorite runtime:

node server.mjs

TypeScript

Each entry only pulls in the types of the runtime it targets. The ambient type packages below are declared as optional peerDependencies, so nothing is installed unless you ask for it:

RuntimeTypes packageNeeded by
Node.js@types/nodesrvx/node
Deno@types/denosrvx/deno
Bun@types/bunsrvx/bun
Cloudflare Workers@cloudflare/workers-typessrvx/cloudflare
AWS Lambda@types/aws-lambdasrvx/aws-lambda

Everything else — srvx/types, srvx/service-worker, srvx/static, srvx/log, srvx/body-limit, srvx/tracing, srvx/mtls and srvx/cli — needs none of them, so a project can type check with skipLibCheck: false:

server.ts
import { serve } from "srvx/node"; // only needs `@types/node`
import { staticMiddleware } from "srvx/static"; // needs nothing
import type { ServerRequest } from "srvx/types"; // needs nothing

Runtime specific fields such as server.node, request.runtime.deno or the bun option are typed as soon as the types of that runtime are loaded, and stay loosely typed (Record<string, unknown>) otherwise — importing srvx/<runtime> anywhere in your project is enough to type them everywhere.

@types/deno references DOM types, so pair it with lib: ["esnext", "dom"].
The universal srvx entry covers every runtime, so it resolves all of the packages above at once. It also references cloudflare:workers, which @cloudflare/workers-types declares ambiently and therefore only resolves when that package is listed in tsconfig types — which in turn conflicts with @types/deno. Declare the module yourself to check that entry with skipLibCheck: false:
declare module "cloudflare:workers" {
  export const env: Record<string, unknown>;
}

Starter Examples

ExampleSourceTry
aws-lambdaexamples/aws-lambdanpx giget gh:h3js/srvx/examples/aws-lambda srvx-aws-lambda
elysiaexamples/elysianpx giget gh:h3js/srvx/examples/elysia srvx-elysia
expressexamples/expressnpx giget gh:h3js/srvx/examples/express srvx-express
fastifyexamples/fastifynpx giget gh:h3js/srvx/examples/fastify srvx-fastify
h3examples/h3npx giget gh:h3js/srvx/examples/h3 srvx-h3
hello-worldexamples/hello-worldnpx giget gh:h3js/srvx/examples/hello-world srvx-hello-world
honoexamples/hononpx giget gh:h3js/srvx/examples/hono srvx-hono
jsxexamples/jsxnpx giget gh:h3js/srvx/examples/jsx srvx-jsx
node-handlerexamples/node-handlernpx giget gh:h3js/srvx/examples/node-handler srvx-node-handler
service-workerexamples/service-workernpx giget gh:h3js/srvx/examples/service-worker srvx-service-worker
streamingexamples/streamingnpx giget gh:h3js/srvx/examples/streaming srvx-streaming
tracingexamples/tracingnpx giget gh:h3js/srvx/examples/tracing srvx-tracing
websocketexamples/websocketnpx giget gh:h3js/srvx/examples/websocket srvx-websocket