Advanced usage
Fetch handler, error handling, proxies, and dynamic route registration.
Fetch handler
server.fetch is a standard (Request, info?) => Promise<Response>. Use it with Deno.serve or for portless tests:
Deno.serve({ port: 5050, hostname: "127.0.0.1" }, server.fetch);
Deno.test("GET /users/:id", async () => {
const response = await server.fetch(new Request("http://localhost/users/42"));
assertEquals((await response.json()).id, "42");
});The optional second argument carries the TCP remote address. When absent (as in tests), req.ip falls back to x-forwarded-for if trustProxy is enabled, otherwise null. Full recipe: Testing without a port.
Custom 404
server.notFound((req, res) =>
res.status(404).json({
error: "Not Found",
path: req.url,
method: req.method,
})
);A handler that returns nothing falls back to the default { success: false, error: "404 Not Found." }.
Error handling
Any error thrown by a middleware, a handler, or the validation layer’s unexpected failures is caught and forwarded to the global error handler. Schema validation failures return 400 before the handler and do not go through onError.
server.onError((error, req, res) =>
res.status(500).json({
success: false,
error: "Internal Server Error",
message: error instanceof Error ? error.message : String(error),
path: req.url,
})
);Returning nothing from onError falls back to { success: false, error: "500 Internal Server Error." }. See Errors & notFound.
Client IP & reverse proxies
req.ip is the remote address of the TCP connection by default, which cannot be spoofed by headers. Behind a trusted reverse proxy, enable trustProxy to read the first entry of x-forwarded-for instead:
const server = new HttpServer({ trustProxy: true });
server.get("/whoami", (req, res) => res.json({ ip: req.ip }));Dynamic routes
addRoute() registers a route from a plain object, with the same typing as get / post / …: schemas, middleware chain, params inference, and the response shape all feed into HttpClient. It also accepts an optional cors field for per-route CORS.
import { HttpMethods, HttpServer, z } from "jsr:@webtools/expressapi";
const server = new HttpServer()
.addRoute({
url: "/users/:id",
method: HttpMethods.GET,
requestListener: (req, res) => res.json({ id: req.params.id }),
})
.addRoute({
url: "/users",
method: HttpMethods.POST,
requestListener: (req, res) => res.status(201).json({ name: req.body.name }),
schemas: { body: z.object({ name: z.string() }) },
cors: { allowOrigin: "https://app.example.com" },
});