> ## Documentation Index
> Fetch the complete documentation index at: https://docs.buntime.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Applications

> Build and preview web apps with buntime.sh

## Overview

buntime.sh makes it easy to build and preview web applications. Every session gets a unique preview URL, and Bun 1.3's built-in hot module reloading (HMR) automatically updates your app when code changes.

## Quick Start

### Create a Simple Server

```typescript theme={null}
import { Buntime } from 'buntime.sh';

const client = new Buntime({ apiKey: process.env.BUNTIME_API_KEY });

// Create a session
const session = await client.sessions.create();

// Write a simple server
await client.files.write({
  sessionId: session.id,
  path: 'server.ts',
  content: `
    Bun.serve({
      port: 8080,
      fetch(req) {
        return new Response("Hello from buntime!");
      }
    });
    
    console.log("Server running on port 8080");
  `
});

// Start the server
await client.execute({
  sessionId: session.id,
  command: 'bun run server.ts'
});

// Your app is live at:
console.log(`Preview URL: https://${session.id}.buntime.sh`);
```

## Preview URLs

Every session gets a unique subdomain:

```
https://{sessionId}.buntime.sh
```

Key features:

* ✅ Always uses port 8080 (required)
* ✅ HTTPS enabled by default
* ✅ Proxied through Cloudflare for security
* ✅ Stays active as long as the session exists
* ✅ No configuration needed

<Warning>
  Your web server **must** listen on port 8080. Other ports are not accessible via the preview URL.
</Warning>

## Hot Module Reloading

Bun 1.3 includes built-in HMR that works automatically:

```typescript theme={null}
// Write initial server
await client.files.write({
  sessionId: session.id,
  path: 'app.ts',
  content: `
    Bun.serve({
      port: 8080,
      fetch(req) {
        return new Response("Version 1");
      }
    });
  `
});

// Start the server
await client.execute({
  sessionId: session.id,
  command: 'bun --hot app.ts' // Enable hot reload
});

// Update the code - HMR automatically reloads!
await client.files.write({
  sessionId: session.id,
  path: 'app.ts',
  content: `
    Bun.serve({
      port: 8080,
      fetch(req) {
        return new Response("Version 2 - Updated!");
      }
    });
  `
});

// Visit preview URL - changes are live instantly
```

<Tip>
  Use the `--hot` flag when starting your server to enable automatic hot reloading.
</Tip>

## Static HTML

You can serve static HTML directly with Bun:

```typescript theme={null}
await client.files.write({
  sessionId: session.id,
  path: 'index.html',
  content: `
    <!DOCTYPE html>
    <html>
      <head>
        <title>My App</title>
        <style>
          body {
            font-family: system-ui;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
          }
        </style>
      </head>
      <body>
        <h1>Hello from buntime.sh!</h1>
        <p>This is a static HTML page.</p>
      </body>
    </html>
  `
});

await client.files.write({
  sessionId: session.id,
  path: 'server.ts',
  content: `
    Bun.serve({
      port: 8080,
      async fetch(req) {
        const url = new URL(req.url);
        const path = url.pathname === '/' ? '/index.html' : url.pathname;
        
        try {
          const file = Bun.file('.' + path);
          return new Response(file);
        } catch {
          return new Response('Not Found', { status: 404 });
        }
      }
    });
  `
});

await client.execute({
  sessionId: session.id,
  command: 'bun --hot server.ts'
});
```

## React Application

Build a full React app with JSX support:

```typescript theme={null}
// Write React component
await client.files.write({
  sessionId: session.id,
  path: 'App.tsx',
  content: `
    import { useState } from 'react';
    
    export function App() {
      const [count, setCount] = useState(0);
      
      return (
        <div style={{ fontFamily: 'system-ui', padding: '50px' }}>
          <h1>React + buntime.sh</h1>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>
            Increment
          </button>
        </div>
      );
    }
  `
});

// Write server with React rendering
await client.files.write({
  sessionId: session.id,
  path: 'server.tsx',
  content: `
    import { renderToReadableStream } from 'react-dom/server';
    import { App } from './App';
    
    Bun.serve({
      port: 8080,
      async fetch(req) {
        const stream = await renderToReadableStream(<App />);
        
        return new Response(stream, {
          headers: { 'Content-Type': 'text/html' }
        });
      }
    });
    
    console.log('React app running on port 8080');
  `
});

// Install React (Bun auto-installs on import, but explicit install is faster)
await client.execute({
  sessionId: session.id,
  command: 'bun add react react-dom'
});

// Start the server
await client.execute({
  sessionId: session.id,
  command: 'bun --hot server.tsx'
});
```

## API Routes

Create a JSON API:

```typescript theme={null}
await client.files.write({
  sessionId: session.id,
  path: 'api.ts',
  content: `
    interface User {
      id: number;
      name: string;
      email: string;
    }
    
    const users: User[] = [
      { id: 1, name: 'Alice', email: 'alice@example.com' },
      { id: 2, name: 'Bob', email: 'bob@example.com' }
    ];
    
    Bun.serve({
      port: 8080,
      fetch(req) {
        const url = new URL(req.url);
        
        // GET /api/users
        if (url.pathname === '/api/users' && req.method === 'GET') {
          return Response.json(users);
        }
        
        // GET /api/users/:id
        const userMatch = url.pathname.match(/^\/api\/users\/(\d+)$/);
        if (userMatch && req.method === 'GET') {
          const id = parseInt(userMatch[1]);
          const user = users.find(u => u.id === id);
          
          if (user) {
            return Response.json(user);
          }
          return Response.json({ error: 'User not found' }, { status: 404 });
        }
        
        // POST /api/users
        if (url.pathname === '/api/users' && req.method === 'POST') {
          const body = await req.json();
          const newUser = {
            id: users.length + 1,
            name: body.name,
            email: body.email
          };
          users.push(newUser);
          return Response.json(newUser, { status: 201 });
        }
        
        return Response.json({ error: 'Not found' }, { status: 404 });
      }
    });
    
    console.log('API server running on port 8080');
  `
});

await client.execute({
  sessionId: session.id,
  command: 'bun --hot api.ts'
});
```

## WebSocket Server

Build real-time apps with WebSockets:

```typescript theme={null}
await client.files.write({
  sessionId: session.id,
  path: 'websocket.ts',
  content: `
    const clients = new Set();
    
    Bun.serve({
      port: 8080,
      fetch(req, server) {
        const url = new URL(req.url);
        
        // WebSocket upgrade
        if (url.pathname === '/ws') {
          const upgraded = server.upgrade(req);
          if (!upgraded) {
            return new Response('WebSocket upgrade failed', { status: 400 });
          }
          return undefined;
        }
        
        // Serve HTML client
        return new Response(\`
          <!DOCTYPE html>
          <html>
            <body>
              <h1>WebSocket Chat</h1>
              <div id="messages"></div>
              <input id="input" type="text" placeholder="Type a message..." />
              <button onclick="send()">Send</button>
              
              <script>
                const ws = new WebSocket('wss://' + location.host + '/ws');
                const messages = document.getElementById('messages');
                const input = document.getElementById('input');
                
                ws.onmessage = (e) => {
                  const p = document.createElement('p');
                  p.textContent = e.data;
                  messages.appendChild(p);
                };
                
                function send() {
                  ws.send(input.value);
                  input.value = '';
                }
                
                input.addEventListener('keypress', (e) => {
                  if (e.key === 'Enter') send();
                });
              </script>
            </body>
          </html>
        \`, {
          headers: { 'Content-Type': 'text/html' }
        });
      },
      websocket: {
        open(ws) {
          clients.add(ws);
          console.log('Client connected. Total:', clients.size);
        },
        message(ws, message) {
          // Broadcast to all clients
          for (const client of clients) {
            client.send(message);
          }
        },
        close(ws) {
          clients.delete(ws);
          console.log('Client disconnected. Total:', clients.size);
        }
      }
    });
    
    console.log('WebSocket server running on port 8080');
  `
});

await client.execute({
  sessionId: session.id,
  command: 'bun --hot websocket.ts'
});
```

## File Uploads

Handle file uploads:

```typescript theme={null}
await client.files.write({
  sessionId: session.id,
  path: 'upload.ts',
  content: `
    Bun.serve({
      port: 8080,
      async fetch(req) {
        const url = new URL(req.url);
        
        // Serve upload form
        if (url.pathname === '/' && req.method === 'GET') {
          return new Response(\`
            <!DOCTYPE html>
            <html>
              <body>
                <h1>File Upload</h1>
                <form action="/upload" method="POST" enctype="multipart/form-data">
                  <input type="file" name="file" required />
                  <button type="submit">Upload</button>
                </form>
              </body>
            </html>
          \`, {
            headers: { 'Content-Type': 'text/html' }
          });
        }
        
        // Handle file upload
        if (url.pathname === '/upload' && req.method === 'POST') {
          const formData = await req.formData();
          const file = formData.get('file') as File;
          
          if (!file) {
            return Response.json({ error: 'No file provided' }, { status: 400 });
          }
          
          // Save file
          await Bun.write(\`uploads/\${file.name}\`, file);
          
          return Response.json({
            message: 'File uploaded successfully',
            filename: file.name,
            size: file.size,
            type: file.type
          });
        }
        
        return new Response('Not Found', { status: 404 });
      }
    });
    
    console.log('Upload server running on port 8080');
  `
});

// Create uploads directory
await client.execute({
  sessionId: session.id,
  command: 'mkdir -p uploads'
});

await client.execute({
  sessionId: session.id,
  command: 'bun --hot upload.ts'
});
```

## Environment Variables

Pass secrets and config to your web app:

```typescript theme={null}
await client.execute({
  sessionId: session.id,
  files: [
    {
      path: 'app.ts',
      content: `
        Bun.serve({
          port: 8080,
          fetch(req) {
            return Response.json({
              environment: process.env.NODE_ENV,
              apiKey: process.env.API_KEY ? '***' : 'not set'
            });
          }
        });
      `
    }
  ],
  command: 'bun --hot app.ts',
  env: {
    NODE_ENV: 'production',
    API_KEY: 'secret_key_123'
  }
});
```

## CORS Headers

Enable CORS for external API access:

```typescript theme={null}
await client.files.write({
  sessionId: session.id,
  path: 'cors.ts',
  content: `
    Bun.serve({
      port: 8080,
      fetch(req) {
        // Handle preflight
        if (req.method === 'OPTIONS') {
          return new Response(null, {
            headers: {
              'Access-Control-Allow-Origin': '*',
              'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
              'Access-Control-Allow-Headers': 'Content-Type, Authorization'
            }
          });
        }
        
        // Handle actual request
        const data = { message: 'Hello from CORS-enabled API' };
        return Response.json(data, {
          headers: {
            'Access-Control-Allow-Origin': '*'
          }
        });
      }
    });
  `
});
```

## Debugging Tips

<AccordionGroup>
  <Accordion icon="magnifying-glass" title="Check if server is running">
    List active processes to see if your server started:

    ```typescript theme={null}
    const info = await client.sessions.info({ sessionId: session.id });
    console.log('Active processes:', info.activeProcesses);
    ```
  </Accordion>

  <Accordion icon="terminal" title="View server logs">
    Execution output includes console.log from your server:

    ```typescript theme={null}
    const result = await client.execute({
      sessionId: session.id,
      command: 'bun run server.ts'
    });
    console.log('Server output:', result.stdout);
    console.log('Server errors:', result.stderr);
    ```
  </Accordion>

  <Accordion icon="network-wired" title="Test locally first">
    Test your server code locally before deploying:

    ```bash theme={null}
    bun run server.ts
    # Visit http://localhost:8080
    ```
  </Accordion>

  <Accordion icon="rotate" title="Restart the server">
    Kill the process and start fresh:

    ```typescript theme={null}
    await client.execution.kill({ sessionId: session.id });
    await client.execute({
      sessionId: session.id,
      command: 'bun --hot server.ts'
    });
    ```
  </Accordion>
</AccordionGroup>

## Limitations

<Warning>
  **Important limitations:**

  * Only port 8080 is accessible via preview URL
  * Preview URLs require the session to be active
  * No custom domains (use the provided subdomain)
  * WebSocket connections close when session sleeps
  * Maximum 1 web server per session
</Warning>

## Best Practices

<CardGroup cols={2}>
  <Card title="Use --hot flag" icon="fire">
    Enable hot reload for faster iteration during development
  </Card>

  <Card title="Handle errors" icon="triangle-exclamation">
    Return proper error responses with status codes
  </Card>

  <Card title="Log to stdout" icon="terminal">
    Use console.log for debugging—output is captured in execute results
  </Card>

  <Card title="Test endpoints" icon="vial">
    Verify your API routes work before sharing preview URLs
  </Card>
</CardGroup>

## Examples

<CardGroup cols={2}>
  <Card title="Express Server" icon="node" href="https://github.com/buntimesh/examples/tree/main/express">
    Full Express.js application
  </Card>

  <Card title="React SSR" icon="react" href="https://github.com/buntimesh/examples/tree/main/react-ssr">
    Server-side rendered React app
  </Card>

  <Card title="REST API" icon="brackets-curly" href="https://github.com/buntimesh/examples/tree/main/rest-api">
    Complete CRUD API example
  </Card>

  <Card title="WebSocket Chat" icon="comments" href="https://github.com/buntimesh/examples/tree/main/websocket-chat">
    Real-time chat application
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Databases" icon="database" href="/guides/databases">
    Add databases to your web app
  </Card>

  <Card title="Multi-file Projects" icon="folder" href="/guides/multi-file-projects">
    Organize larger applications
  </Card>

  <Card title="Environment Variables" icon="key" href="/guides/environment-variables">
    Manage secrets and configuration
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/execution/execute">
    Explore execution options
  </Card>
</CardGroup>
