serveDir from @std/http for static files
Example of serving static files:
```ts
import { serveDir } from "jsr:@std/http/file-server";
Deno.serve((req) => serveDir(req, { fsRoot: "./public" }));
```
The serveDir function handles content types, range requests, and directory traversal protection. Run with read access: deno run -N -R server.ts. It can also be used as a CLI: deno run -RN jsr:@std/http/file-server ./public
Deno.serve basic handler returns Response
The Deno.serve function takes a handler function that receives an incoming request and must return a Response or Promise<Response>. The handler can be a regular function or an async function.
Default port for Deno.serve is 8000
When using Deno.serve without specifying a port, it listens on port 8000 by default.
Deno.serve port and hostname options
Deno.serve accepts an options bag as the first or second argument. The port option sets the listening port. The hostname option sets the bind address, for example '0.0.0.0' to bind to all interfaces.
Hello World HTTP server example
Example of a basic HTTP server:
```ts
Deno.serve((_req) => {
return new Response("Hello, World!");
});
```
This example shows the simplest HTTP server that returns the same response for every request.
Run server with --allow-net flag
To run an HTTP server, use the command: deno run --allow-net server.ts. The --allow-net flag grants network access permission required by Deno.serve.
Inspect request method, path, query params, headers, body
Example of extracting request details:
```ts
Deno.serve(async (req) => {
console.log("Method:", req.method);
const url = new URL(req.url);
console.log("Path:", url.pathname);
console.log("Query parameters:", url.searchParams);
console.log("Headers:", req.headers);
if (req.body) {
const body = await req.text();
console.log("Body:", body);
}
return new Response("Hello, World!");
});
```
This shows how to access the HTTP method, URL pathname, query string parameters, headers, and request body from the incoming request.
Request body methods can fail on client disconnect
Calling req.text(), req.json(), req.formData(), req.arrayBuffer(), req.body.getReader().read(), or req.body.pipeTo() can fail if the user hangs up the connection before the body is fully received. Always handle these potential errors.
Response with status code, headers, and JSON body example
Example of returning a 404 response with JSON body and custom headers:
```ts
Deno.serve((req) => {
const body = JSON.stringify({ message: "NOT FOUND" });
return new Response(body, {
status: 404,
headers: {
"content-type": "application/json; charset=utf-8",
},
});
});
```
Response streaming with ReadableStream
Example of a response that returns a stream of data:
```ts
Deno.serve((req) => {
let timer: number;
const body = new ReadableStream({
async start(controller) {
timer = setInterval(() => {
controller.enqueue("Hello, World!\n");
}, 1000);
},
cancel() {
clearInterval(timer);
},
});
return new Response(body.pipeThrough(new TextEncoderStream()), {
headers: {
"content-type": "text/plain; charset=utf-8",
},
});
});
```
The cancel function is called when the client hangs up the connection and must clean up resources.
Handle stream cancellation on client disconnect
When a client hangs up the connection, the response body stream is cancelled. The cancel function in a ReadableStream or errors in write() calls on a WritableStream must be handled, otherwise the server will keep queuing up messages and eventually run out of memory.
URLPattern for routing requests
Example of routing with URLPattern:
```ts
const userPattern = new URLPattern({ pathname: "/users/:id" });
Deno.serve((req) => {
const match = userPattern.exec(req.url);
if (match) {
const id = match.pathname.groups.id;
return new Response(`User ${id}`);
}
if (new URL(req.url).pathname === "/") {
return new Response("Home");
}
return new Response("Not found", { status: 404 });
});
```
URLPattern is a built-in web API for matching URL patterns.
Connection cannot be reused after WebSocket upgrade
The connection that a WebSocket was created on cannot be used for HTTP traffic after a WebSocket upgrade has been performed.
Graceful shutdown with Deno.serve return value
Example of graceful shutdown:
```ts
const server = Deno.serve((_req) => new Response("Hello"));
Deno.addSignalListener("SIGINT", async () => {
console.log("shutting down");
await server.shutdown();
});
```
Deno.serve returns an HttpServer object whose shutdown() method stops accepting new connections while letting in-flight requests finish. Combine with a signal listener for clean exits in production.
Use AbortSignal with Deno.serve signal option
Deno.serve accepts a signal option that can be an AbortSignal to tie the server's lifetime to other logic.
HTTPS support with cert and key options
Example of HTTPS server:
```ts
Deno.serve({
port: 8443,
cert: Deno.readTextFileSync("./cert.pem"),
key: Deno.readTextFileSync("./key.pem"),
}, (_req) => new Response("Hello over HTTPS!"));
```
The cert and key options take PEM-encoded certificate and private key contents as strings, not file paths. Run with: deno run --allow-net --allow-read=cert.pem,key.pem server.ts
Generate self-signed certificate with OpenSSL
Command to generate a short-lived self-signed certificate for local development:
```sh
openssl req -x509 -newkey rsa:2048 -nodes -days 1 \
-keyout key.pem -out cert.pem \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost"
```
HTTP/2 support is automatic
HTTP/2 support is automatic when using the HTTP server APIs with Deno. The server handles HTTP/1 or HTTP/2 requests seamlessly without any configuration. HTTP/2 is also supported over cleartext with prior knowledge.
Enable automatic response body compression
The HTTP server can automatically compress response bodies using gzip or brotli. Enable it for a single server with `automaticCompression: true` option, or for the whole process by setting the environment variable DENO_SERVE_AUTOMATIC_COMPRESSION=1.
Automatic compression conditions
A response body is compressed automatically if: the request has an Accept-Encoding header indicating support for br (Brotli) or gzip (Deno respects quality value preference), the response has a Content-Type considered compressible (derived from jshttp/mime-db), and the response body is greater than 64 bytes.
Automatic compression response headers
When a response body is compressed automatically, Deno sets the Content-Encoding header to reflect the encoding method and adjusts or adds the Vary header to indicate which request headers affected the response.
Prevent automatic compression with response headers
A response will not be compressed automatically if it contains: a Content-Encoding header (indicating encoding already done), a Content-Range header (indicating a range request), or a Cache-Control header with the no-transform value (indicating the server doesn't want Deno or downstream proxies to modify the response).
Deno.upgradeWebSocket for WebSocket support
Example of upgrading an HTTP request to WebSocket:
```ts
Deno.serve((req) => {
if (req.headers.get("upgrade") != "websocket") {
return new Response(null, { status: 426 });
}
const { socket, response } = Deno.upgradeWebSocket(req);
socket.addEventListener("open", () => {
console.log("a client connected!");
});
socket.addEventListener("message", (event) => {
if (event.data === "ping") {
socket.send("pong");
}
});
return response;
});
```
The Deno.upgradeWebSocket function returns an object with a socket (WebSocket object) and a response to return to the incoming request.
WebSockets only supported on HTTP/1.1
WebSockets are only supported on HTTP/1.1 for now, not on HTTP/2.
Default fetch export for HTTP server
Example of creating an HTTP server with default fetch export:
```ts
export default {
fetch(request) {
const userAgent = request.headers.get("user-agent") || "Unknown";
return new Response(`User Agent: ${userAgent}`);
},
} satisfies Deno.ServeDefaultExport;
```
This file can be run with: deno serve server.ts. Add `satisfies Deno.ServeDefaultExport` for proper type-checking.
Request abort signal legacy behavior deprecation
For historical reasons, Deno.serve fires the abort event on a request's signal even when the handler returns successfully. This trips up some Node proxy libraries like http-proxy. Pass --unstable-no-legacy-abort to opt into corrected behavior where signal only aborts when the client actually disconnects. Relying on legacy behavior now prints a deprecation warning.
Automatic HTTP tracing with Deno.serve
When using Deno.serve to create an HTTP server, a span is automatically created for each incoming request. The span ends when response headers are sent (not when the response body is complete). The span name is ${method} and the span kind is 'server'.
Automatic HTTP tracing with fetch
When using fetch to make an HTTP request, a span is automatically created for the request. The span ends when response headers are received. The span name is ${method} and the span kind is 'client'.
Deno.serve span attributes for HTTP requests
For incoming HTTP requests with Deno.serve, the following attributes are automatically added to spans on creation: http.request.method, url.full, url.scheme, url.path, url.query. After the request is handled, http.response.status_code is added.
fetch span attributes for HTTP requests
For outgoing HTTP requests with fetch, the following attributes are automatically added to spans on creation: http.request.method, url.full, url.scheme, url.path, url.query. After the response is received, http.status_code is added.
HTTP server request duration metric
The metric http.server.request.duration is a histogram measuring the duration of incoming HTTP requests served with Deno.serve or Deno.serveHttp, from when the request is received to when response headers are sent. Unit is seconds. Histogram buckets are [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0].
HTTP server request duration metric attributes
The http.server.request.duration metric includes these attributes: http.request.method, url.scheme, network.protocol.version, server.address, server.port, http.response.status_code (if no fatal error), and error.type (if error occurred).
HTTP server active requests metric
The metric http.server.active_requests is a gauge measuring the number of active requests being handled by Deno.serve or Deno.serveHttp at any given time. It counts requests that have been received but not yet responded to (response headers not yet sent).
HTTP server active requests metric attributes
The http.server.active_requests metric includes these attributes: http.request.method, url.scheme, server.address, server.port.
HTTP server response body size metric
The metric http.server.response.body.size is a histogram measuring the size of response bodies of incoming HTTP requests served with Deno.serve or Deno.serveHttp. Unit is bytes. Histogram buckets are [0, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000].
OpenTelemetry propagation with fetch and Deno.serve
The W3C Trace Context and W3C Baggage propagators automatically work with Deno's fetch API and Deno.serve, enabling end-to-end tracing across HTTP requests without manual context management.
Example: Add route attribute to HTTP span with Deno.serve
import { trace } from "npm:@opentelemetry/api@1";
const INDEX_ROUTE = new URLPattern({ pathname: "/" });
const BOOK_ROUTE = new URLPattern({ pathname: "/book/:id" });
Deno.serve(async (req) => {
const span = trace.getActiveSpan();
if (INDEX_ROUTE.test(req.url)) {
span.setAttribute("http.route", "/");
span.updateName(`${req.method} /`);
// handle index route
} else if (BOOK_ROUTE.test(req.url)) {
span.setAttribute("http.route", "/book/:id");
span.updateName(`${req.method} /book/:id`);
// handle book route
} else {
return new Response("Not found", { status: 404 });
}
});
This example shows how to manually add http.route attributes and update span names for HTTP handlers.
Next.js setup with Deno
To create a new Next.js app with Deno, run: deno run -A npm:create-next-app@latest my-next-app, then cd my-next-app, then deno task dev. The app opens at http://localhost:3000. TypeScript is included by default. Edit page.tsx to see changes live.
HTTP server example with Deno and Fresh
To get started with a Fresh app, run the command: deno run -Ar jsr:@fresh/init, then cd my-fresh-app, then deno task dev. This creates a new Fresh app and runs it with Deno. The app opens in the browser at http://localhost:8000. Edit /routes/index.tsx to see changes live.
Astro static site generator with Deno
To create a new Astro site with Deno, run: deno run -A npm:create-astro my-astro-site, then cd my-astro-site, then deno task dev. The site opens at http://localhost:4321. Edit /src/pages/index.astro to see changes live.
Vite setup with Deno
To create a new Vite app with Deno, run: deno run -A npm:create-vite@latest, then cd my-vite-app, then deno install, then deno task dev.
Lume static site generator setup with Deno
To create a new Lume site with Deno, run: mkdir my-lume-site, cd my-lume-site, then deno run -A https://lume.land/init.ts, then deno task serve.
Docusaurus setup with Deno
To create a new Docusaurus site with Deno, run: deno run -A npm:create-docusaurus@latest my-website classic, then cd my-website, then deno task start.
Hono web framework setup with Deno
To create a new Hono app with Deno, run: deno run -A npm:create-hono@latest, then cd my-hono-app, then deno task start. The app opens at http://localhost:8000.
Oak middleware framework HTTP server example
Oak is a middleware framework for handling HTTP with Deno. To create a basic server, create a file called server.ts with: import { Application } from "jsr:@oak/oak/application"; import { Router } from "jsr:@oak/oak/router"; const router = new Router(); router.get("/", (ctx) => { ctx.response.body = `<!DOCTYPE html><html><head><title>Hello oak!</title><head><body><h1>Hello oak!</h1></body></html>`; }); const app = new Application(); const port = 8080; app.use(router.routes()); app.use(router.allowedMethods()); console.log(`Server running on http://localhost:${port}`); app.listen({ port: port }); Run it with: deno run --allow-net server.ts
Fresh framework sends no JavaScript to clients by default
Fresh is the most popular web framework for Deno. It uses a model where no JavaScript is sent to clients by default. Fresh does most of its rendering on the server, with the client only responsible for re-rendering small islands of interactivity. Developers explicitly opt in to client-side rendering for specific components.
Astro is a static site generator for fast, lightweight websites
Astro is a static site generator that allows developers to create fast and lightweight websites.
Oak framework offers router, JSON parser, middlewares, and plugins
Oak is a middleware framework for handling HTTP with Deno. Oak offers additional functionality over the native Deno HTTP server, including a basic router, JSON parser, middlewares, and plugins.
Deno KV HTTP server example with Deno.serve
Here is an example of creating an HTTP server with Deno KV: const kv = await Deno.openKv(); Deno.serve(async () => { const res = await kv.get<number>(["requests"]); const requests = res.value + 1; await kv.set(["requests"], requests); return new Response(JSON.stringify(requests)); });
Deno.serve basic HTTP server example
To create a basic HTTP server with Deno.serve, pass a handler function that receives a request and returns a Response. Example: Deno.serve((_req) => { return new Response("Hello, World!"); }); By default, Deno.serve listens on port 8000, but this can be changed by passing a port number in an options bag as the first or second argument.
Deno HTTP Server APIs: Deno.serve vs Deno.serveHttp
Deno has two HTTP Server APIs: Deno.serve is a native, higher-level API that supports HTTP/1.1 and HTTP2 and is the preferred API to write HTTP servers in Deno. Deno.serveHttp is a native, low-level API that supports HTTP/1.1 and HTTP2.