home / skills / secondsky / claude-skills / bun-http-server

bun-http-server skill

/plugins/bun/skills/bun-http-server

This skill helps you build high-performance Bun HTTP servers, handle requests, routing, and responses with practical code patterns.

npx playbooks add skill secondsky/claude-skills --skill bun-http-server

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
5.7 KB
---
name: Bun HTTP Server
description: Use when building HTTP servers with Bun.serve, handling requests/responses, implementing routing, creating REST APIs, or configuring fetch handlers.
version: 1.0.0
---

# Bun HTTP Server

Bun has a built-in high-performance HTTP server via `Bun.serve()`.

## Quick Start

```typescript
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello World!");
  },
});

console.log(`Server running at http://localhost:${server.port}`);
```

## Request Handling

```typescript
Bun.serve({
  fetch(req) {
    const url = new URL(req.url);

    // Method
    console.log(req.method); // GET, POST, etc.

    // Path
    console.log(url.pathname); // /api/users

    // Query params
    console.log(url.searchParams.get("id")); // ?id=123

    // Headers
    console.log(req.headers.get("Content-Type"));

    return new Response("OK");
  },
});
```

## Body Parsing

```typescript
Bun.serve({
  async fetch(req) {
    // JSON
    const json = await req.json();

    // Form data
    const form = await req.formData();

    // Text
    const text = await req.text();

    // ArrayBuffer
    const buffer = await req.arrayBuffer();

    // Blob
    const blob = await req.blob();

    return new Response("Received");
  },
});
```

## Response Types

```typescript
Bun.serve({
  fetch(req) {
    const url = new URL(req.url);

    switch (url.pathname) {
      case "/json":
        return Response.json({ message: "Hello" });

      case "/html":
        return new Response("<h1>Hello</h1>", {
          headers: { "Content-Type": "text/html" },
        });

      case "/redirect":
        return Response.redirect("/new-location", 302);

      case "/file":
        return new Response(Bun.file("./image.png"));

      case "/stream":
        return new Response(
          new ReadableStream({
            start(controller) {
              controller.enqueue("chunk1");
              controller.enqueue("chunk2");
              controller.close();
            },
          })
        );

      default:
        return new Response("Not Found", { status: 404 });
    }
  },
});
```

## Simple Routing

```typescript
Bun.serve({
  fetch(req) {
    const url = new URL(req.url);
    const path = url.pathname;
    const method = req.method;

    // Static routes
    if (method === "GET" && path === "/") {
      return new Response("Home");
    }

    if (method === "GET" && path === "/api/users") {
      return Response.json([{ id: 1, name: "Alice" }]);
    }

    // Dynamic routes
    const userMatch = path.match(/^\/api\/users\/(\d+)$/);
    if (method === "GET" && userMatch) {
      const userId = userMatch[1];
      return Response.json({ id: userId });
    }

    // 404
    return new Response("Not Found", { status: 404 });
  },
});
```

## Error Handling

```typescript
Bun.serve({
  fetch(req) {
    try {
      // Handle request
      throw new Error("Something went wrong");
    } catch (error) {
      return new Response(
        JSON.stringify({ error: error.message }),
        {
          status: 500,
          headers: { "Content-Type": "application/json" }
        }
      );
    }
  },
  error(error) {
    // Global error handler
    return new Response(`Error: ${error.message}`, { status: 500 });
  },
});
```

## Server Options

```typescript
const server = Bun.serve({
  port: 3000,           // Default: 3000
  hostname: "0.0.0.0",  // Default: "0.0.0.0"

  development: true,    // Pretty errors in browser

  // TLS/HTTPS
  tls: {
    key: Bun.file("./key.pem"),
    cert: Bun.file("./cert.pem"),
  },

  // Unix socket
  unix: "/tmp/my-socket.sock",

  // Max request body size (default 128MB)
  maxRequestBodySize: 1024 * 1024 * 10, // 10MB

  fetch(req) {
    return new Response("OK");
  },
});
```

## Server Methods

```typescript
const server = Bun.serve({ ... });

// Server info
console.log(server.port);        // 3000
console.log(server.hostname);    // "0.0.0.0"
console.log(server.url);         // URL object

// Stop server
server.stop();

// Reload with new config
server.reload({
  fetch(req) {
    return new Response("Updated!");
  },
});

// Pending requests count
console.log(server.pendingRequests);
```

## Static Files

```typescript
Bun.serve({
  fetch(req) {
    const url = new URL(req.url);

    // Serve static files from public/
    if (url.pathname.startsWith("/static/")) {
      const filePath = `./public${url.pathname.replace("/static", "")}`;
      const file = Bun.file(filePath);

      if (await file.exists()) {
        return new Response(file);
      }
      return new Response("Not Found", { status: 404 });
    }

    return new Response("API");
  },
});
```

## CORS

```typescript
function corsHeaders() {
  return {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type, Authorization",
  };
}

Bun.serve({
  fetch(req) {
    // Handle preflight
    if (req.method === "OPTIONS") {
      return new Response(null, { headers: corsHeaders() });
    }

    // Add CORS headers to response
    return new Response("OK", { headers: corsHeaders() });
  },
});
```

## Common Errors

| Error | Cause | Fix |
|-------|-------|-----|
| `EADDRINUSE` | Port in use | Use different port or kill process |
| `Cannot read body` | Body already consumed | Read body once only |
| `CORS error` | Missing headers | Add CORS headers |
| `413 Payload Too Large` | Body exceeds limit | Increase maxRequestBodySize |

## When to Load References

Load `references/tls-config.md` when:
- HTTPS/TLS setup
- Certificate configuration
- mTLS authentication

Load `references/streaming.md` when:
- Server-sent events
- Streaming responses
- Chunked transfer encoding

Overview

This skill provides practical guidance and ready patterns for building high-performance HTTP servers with Bun.serve in TypeScript. It covers request parsing, response types, simple routing, error handling, static file serving, CORS, TLS, and useful server options. Use it to quickly implement REST APIs, fetch handlers, and streaming responses with Bun.

How this skill works

The skill demonstrates creating a Bun server via Bun.serve and supplying a fetch(req) handler that inspects the incoming Request object. It shows parsing method, path, query parameters, headers, and the body via json(), formData(), text(), arrayBuffer(), or blob(). Responses include JSON, HTML, redirects, files, and ReadableStream-based streaming. Additional patterns cover routing, global error handling, server options (port, hostname, TLS, unix sockets), and static file serving.

When to use it

  • Building lightweight REST APIs or microservices with Bun and TypeScript.
  • Implementing request handlers for server-side rendering or API endpoints.
  • Serving static assets from a public directory alongside an API.
  • Creating streaming responses, server-sent events, or chunked outputs.
  • Configuring HTTPS, custom ports, or unix socket bindings for production.

Best practices

  • Read the request body only once; reuse parsed result rather than calling multiple body methods.
  • Return Response.json(...) for JSON APIs and set explicit Content-Type for other payloads.
  • Handle OPTIONS preflight and include CORS headers when exposing APIs to browsers.
  • Use maxRequestBodySize to prevent large payload attacks and tune it per endpoint.
  • Implement try/catch in fetch and an error(error) handler for global failures.

Example use cases

  • A simple GET/POST REST API with dynamic route matching for /api/users/:id.
  • Serving static files under /static/ from a public/ directory with existence checks.
  • An HTTPS server using local TLS cert and key files for secure development or staging.
  • A streaming endpoint that yields multiple chunks via ReadableStream for real-time feeds.
  • A small proxy or middleware that inspects headers and forwards or modifies requests.

FAQ

How do I handle CORS for browser requests?

Respond to OPTIONS preflight with appropriate Access-Control-Allow-* headers and attach the same headers to actual responses.

Why do I get 'Cannot read body' errors?

The body stream can be consumed only once. Parse it once (json/formData/text) and reuse the parsed value, or clone the request before consuming if needed.