Skip to main content

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

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
Your web server must listen on port 8080. Other ports are not accessible via the preview URL.

Hot Module Reloading

Bun 1.3 includes built-in HMR that works automatically:
// 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
Use the --hot flag when starting your server to enable automatic hot reloading.

Static HTML

You can serve static HTML directly with Bun:
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:
// 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:
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: '[email protected]' },
      { id: 2, name: 'Bob', email: '[email protected]' }
    ];
    
    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:
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:
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:
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:
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

List active processes to see if your server started:
const info = await client.sessions.info({ sessionId: session.id });
console.log('Active processes:', info.activeProcesses);
Execution output includes console.log from your server:
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);
Test your server code locally before deploying:
bun run server.ts
# Visit http://localhost:8080
Kill the process and start fresh:
await client.execution.kill({ sessionId: session.id });
await client.execute({
  sessionId: session.id,
  command: 'bun --hot server.ts'
});

Limitations

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

Best Practices

Use --hot flag

Enable hot reload for faster iteration during development

Handle errors

Return proper error responses with status codes

Log to stdout

Use console.log for debugging—output is captured in execute results

Test endpoints

Verify your API routes work before sharing preview URLs

Examples

Next Steps