Lesson 11-Bun.js Advanced Application Development

Server-Side Rendering (SSR)

SSR Core Implementation

Bun.js SSR Core Architecture:

// src/server/render.ts
import { serve } from 'bun'
import React from 'react'
import { renderToString } from 'react-dom/server'
import App from '../app' // React application entry point

const server = serve({
  port: 3000,
  fetch(request) {
    // 1. Parse request URL to get route information
    const url = new URL(request.url)

    // 2. Data prefetching (simulated API call)
    const prefetchData = async () => {
      // Replace with actual API call in real projects
      return { user: { name: 'Bun User', id: 1 } }
    }

    // 3. Server-side rendering
    const html = renderToString(
      <App initialData={prefetchData()} url={url.pathname} />
    )

    // 4. Inject initial state into HTML
    const initialState = JSON.stringify(prefetchData())
    const finalHtml = `
      <!DOCTYPE html>
      <html>
        <head>
          <title>Bun SSR App</title>
        </head>
        <body>
          <div id="root">${html}</div>
          <script>window.__INITIAL_STATE__ = ${initialState}</script>
          <script src="/client.js"></script>
        </body>
      </html>
    `

    return new Response(finalHtml, {
      headers: { 'Content-Type': 'text/html' }
    })
  }
})

console.log('SSR Server running on http://localhost:3000')

Template Engine Integration (Edge Template):

// src/server/edgeRender.ts
import { serve } from 'bun'
import { Edge } from 'edge.js' // Assuming Edge template engine is used

const edge = new Edge({
  views: `${process.cwd()}/src/views` // Template directory
})

const server = serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url)

    // Render using Edge template
    const html = edge.render('home', {
      title: 'Bun SSR with Edge',
      user: { name: 'Edge User' }
    })

    return new Response(html, {
      headers: { 'Content-Type': 'text/html' }
    })
  }
})

console.log('Edge SSR Server running on http://localhost:3000')

Data Prefetching and State Synchronization

Data Prefetching Strategy Implementation:

// src/utils/dataFetcher.ts
export async function prefetchRouteData(url: string) {
  // Simulated API requests
  const mockApi = {
    '/': () => Promise.resolve({ featuredPosts: [1, 2, 3] }),
    '/about': () => Promise.resolve({ team: ['Alice', 'Bob'] }),
    '/posts/:id': async (params: { id: string }) => {
      // Simulated post details fetch
      return { 
        post: { 
          id: params.id, 
          title: `Post ${params.id}`,
          content: 'Sample content'
        }
      }
    }
  }

  // Route matching and data prefetching
  if (url === '/') return mockApi['/']()
  if (url === '/about') return mockApi['/about']()

  // Dynamic route handling
  const dynamicMatch = url.match(/^\/posts\/(\d+)$/)
  if (dynamicMatch) {
    return mockApi['/posts/:id']({ id: dynamicMatch[1] })
  }

  return {} // Default to empty data
}

// Usage in SSR flow
const data = await prefetchRouteData(url.pathname)
const html = renderToString(<App initialData={data} />)

Client-Side State Synchronization:

// src/app.tsx (React client entry point)
import React from 'react'
import { hydrateRoot } from 'react-dom/client'

interface AppProps {
  initialData: any
  url: string
}

export default function App({ initialData, url }: AppProps) {
  // Use initial state to avoid duplicate requests
  const [data, setData] = React.useState(initialData)

  React.useEffect(() => {
    // Fetch new data during client navigation
    if (!initialData) {
      prefetchRouteData(url).then(setData)
    }
  }, [url])

  return (
    <div>
      <h1>Bun SSR App</h1>
      {/* Render data... */}
    </div>
  )
}

// Client-side hydration
if (typeof document !== 'undefined') {
  hydrateRoot(
    document.getElementById('root')!,
    <App initialData={window.__INITIAL_STATE__} url={window.location.pathname} />
  )
}

SSR Performance Optimization

Component-Level Caching Strategy:

// src/cache/componentCache.ts
const componentCache = new Map<string, string>()

export function getCachedComponent(key: string, renderFn: () => string) {
  if (componentCache.has(key)) {
    return componentCache.get(key)!
  }

  const html = renderFn()
  componentCache.set(key, html)

  // Set cache expiration (e.g., 10 minutes)
  setTimeout(() => {
    componentCache.delete(key)
  }, 10 * 60 * 1000)

  return html
}

// Usage example
const userHtml = getCachedComponent(`user-${userId}`, () => {
  return renderToString(<UserProfile userId={userId} />)
})

Streaming SSR Implementation:

// src/server/streamingRender.ts
import { serve } from 'bun'
import { renderToPipeableStream } from 'react-dom/server'

const server = serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url)

    // Create writable stream
    const stream = new WritableStream({
      write(chunk) {
        // Send HTML chunks
        response.write(chunk)
      },
      close() {
        response.end()
      }
    })

    const response = new Response(stream, {
      headers: { 'Content-Type': 'text/html' }
    })

    // Stream render React components
    renderToPipeableStream(
      <App url={url.pathname} />,
      {
        onShellReady() {
          // Start sending when initial shell is ready
          response.write('<!DOCTYPE html><html><head><title>Bun SSR</title></head><body><div id="root">')
        },
        onShellError(error) {
          console.error('Shell rendering error:', error)
          response.status = 500
          response.write('Error rendering page')
          response.end()
        },
        onCompleteShell() {
          response.write('</div></body></html>')
        },
        onAllReady() {
          // All content ready (optional)
        }
      },
      stream
    )

    return response
  }
})

SSR Security and Error Handling

Security Measures:

// src/middleware/security.ts
import { Context, Next } from 'bun'

export function securityMiddleware(ctx: Context, next: Next) {
  // 1. XSS Protection
  ctx.response.headers.set('X-XSS-Protection', '1; mode=block')

  // 2. CSP Policy
  ctx.response.headers.set('Content-Security-Policy', [
    "default-src 'self'",
    "script-src 'self' 'unsafe-inline' cdn.example.com",
    "style-src 'self' 'unsafe-inline'",
    "img-src 'self' data:"
  ].join('; '))

  // 3. Clickjacking Protection
  ctx.response.headers.set('X-Frame-Options', 'DENY')

  // 4. MIME Type Sniffing Protection
  ctx.response.headers.set('X-Content-Type-Options', 'nosniff')

  return next()
}

// Usage in SSR flow
server.use(securityMiddleware)

Error Boundary Handling:

// src/components/ErrorBoundary.tsx
import React from 'react'

interface State {
  hasError: boolean
  error?: Error
}

export class ErrorBoundary extends React.Component<{}, State> {
  state: State = { hasError: false }

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error }
  }

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    console.error('SSR Error:', error, info.componentStack)
    // Optionally report error to monitoring system
  }

  render() {
    if (this.state.hasError) {
      // Return fallback UI
      return (
        <div>
          <h1>Something went wrong</h1>
          <p>Please refresh the page or try again later</p>
        </div>
      )
    }

    return this.props.children
  }
}

// Usage in App
export default function App() {
  return (
    <ErrorBoundary>
      {/* Application content */}
    </ErrorBoundary>
  )
}

SSR Use Cases

SEO Optimization Implementation:

// src/server/seoRender.ts
import { serve } from 'bun'

const server = serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url)

    // Dynamically generate SEO metadata
    const seoData = {
      '/': {
        title: 'Home Page - Bun SSR Demo',
        description: 'Welcome to our Bun.js powered website',
        keywords: 'Bun,SSR,JavaScript'
      },
      '/about': {
        title: 'About Us - Bun SSR Demo',
        description: 'Learn more about our company',
        keywords: 'About,Bun,Company'
      }
    }

    const meta = seoData[url.pathname] || {
      title: 'Default Title',
      description: 'Default Description',
      keywords: 'Bun,SSR'
    }

    const html = `
      <!DOCTYPE html>
      <html>
        <head>
          <title>${meta.title}</title>
          <meta name="description" content="${meta.description}">
          <meta name="keywords" content="${meta.keywords}">
        </head>
        <body>
          <div id="root"></div>
          <script src="/client.js"></script>
        </body>
      </html>
    `

    return new Response(html, {
      headers: { 'Content-Type': 'text/html' }
    })
  }
})

First Paint Performance Optimization:

// src/utils/performance.ts
export function optimizeFirstPaint() {
  // 1. Inline critical CSS
  const criticalCSS = `
    body { font-family: sans-serif; margin: 0; }
    .header { background: #fff; padding: 1rem; }
  `

  // 2. Defer non-critical JS
  const lazyJs = `
    <script defer src="/non-critical.js"></script>
  `

  // 3. Lazy-load images
  const lazyImages = `
    <img loading="lazy" src="/image.jpg" alt="Example">
  `

  return {
    criticalCSS,
    lazyJs,
    lazyImages
  }
}

// Integrate in SSR flow
const { criticalCSS, lazyJs, lazyImages } = optimizeFirstPaint()
const html = `
  <!DOCTYPE html>
  <html>
    <head>
      <style>${criticalCSS}</style>
    </head>
    <body>
      <div id="root"></div>
      ${lazyImages}
      ${lazyJs}
    </body>
  </html>
`

Static Site Generation (SSG)

SSG Core Implementation

Bun Build-Time Static File Generation:

// src/server/buildStatic.ts
import { build } from 'bun'
import fs from 'fs/promises'
import path from 'path'
import { renderToString } from 'react-dom/server'
import App from '../app'

async function generateStaticSite() {
  // 1. Define route list
  const routes = [
    '/',
    '/about',
    '/blog/1',
    '/blog/2'
  ]

  // 2. Generate HTML files for each route
  await Promise.all(routes.map(async (route) => {
    // Simulated data fetch
    const data = await getRouteData(route)

    // Render React component to HTML string
    const html = renderToString(<App initialData={data} />)

    // Inject initial state
    const finalHtml = `
      <!DOCTYPE html>
      <html>
        <head>
          <title>Bun SSG App</title>
        </head>
        <body>
          <div id="root">${html}</div>
          <script>window.__INITIAL_STATE__ = ${JSON.stringify(data)}</script>
          <script src="/client.js"></script>
        </body>
      </html>
    `

    // Write to filesystem
    const filePath = path.join(process.cwd(), 'dist', route === '/' ? 'index.html' : `${route}.html`)
    await fs.mkdir(path.dirname(filePath), { recursive: true })
    await fs.writeFile(filePath, finalHtml)
  }))

  console.log('Static site generated in dist directory')
}

// Execute build
build({
  entrypoints: ['src/server/buildStatic.ts'],
  outdir: 'dist-functions' // Optional: for Serverless deployment
})

// Run SSG generation
generateStaticSite()

Dynamic Route Handling:

// src/utils/dynamicRoutes.ts
type RouteParams = {
  [key: string]: string
}

export function matchDynamicRoute(
  routePattern: string,
  urlPath: string
): { matched: boolean; params?: RouteParams } {
  // Convert route pattern to regex
  const paramNames: string[] = []
  const regexPattern = routePattern
    .replace(/:(\w+)/g, (_, paramName) => {
      paramNames.push(paramName)
      return '([^/]+)'
    })
    .replace(/\//g, '\\/')

  const regex = new RegExp(`^${regexPattern}$`)
  const match = urlPath.match(regex)

  if (!match) {
    return { matched: false }
  }

  // Extract parameters
  const params: RouteParams = {}
  paramNames.forEach((name, index) => {
    params[name] = match[index + 1]
  })

  return { matched: true, params }
}

// Usage example
const result = matchDynamicRoute('/blog/:id', '/blog/123')
if (result.matched) {
  console.log('Params:', result.params) // { id: '123' }
}

Content Management and Dynamic Routing

Markdown Content Processing:

// src/content/loadMarkdown.ts
import fs from 'fs/promises'
import path from 'path'
import marked from 'marked' // Markdown parsing library

export async function loadMarkdownFile(filePath: string) {
  try {
    const content = await fs.readFile(filePath, 'utf8')
    const html = marked.parse(content)

    // Extract front matter (e.g., title, date)
    const frontMatterMatch = content.match(/---\n([\s\S]+?)\n---/)
    const frontMatter = frontMatterMatch 
      ? parseFrontMatter(frontMatterMatch[1])
      : {}

    return {
      html,
      frontMatter,
      filePath
    }
  } catch (err) {
    console.error('Failed to load markdown file:', err)
    throw err
  }
}

function parseFrontMatter(raw: string): Record<string, any> {
  return raw.split('\n')
    .filter(line => line.includes(':'))
    .reduce((acc, line) => {
      const [key, value] = line.split(':').map(s => s.trim())
      acc[key] = isNaN(Number(value)) ? value : Number(value)
      return acc
    }, {} as Record<string, any>)
}

// Generate dynamic routes
export async function generateBlogRoutes() {
  const blogDir = path.join(process.cwd(), 'content/blog')
  const files = await fs.readdir(blogDir)

  return Promise.all(
    files.map(async (file) => {
      const filePath = path.join(blogDir, file)
      const { html, frontMatter } = await loadMarkdownFile(filePath)

      return {
        route: `/blog/${frontMatter.slug || file.replace('.md', '')}`,
        content: html,
        frontMatter
      }
    })
  )
}

SSG Performance Optimization

Incremental Static Regeneration (ISR) Simulation:

// src/server/isr.ts
import fs from 'fs/promises'
import path from 'path'

// Simulated cache storage
const cache = new Map<string, { html: string; timestamp: number; ttl: number }>()

export async function getStaticPageWithISR(route: string) {
  const now = Date.now()
  const cacheKey = `page-${route}`

  // Check if cache exists and is valid
  if (cache.has(cacheKey)) {
    const entry = cache.get(cacheKey)!
    if (now - entry.timestamp < entry.ttl) {
      return entry.html // Return cached content
    }
  }

  // Cache invalid or missing, regenerate
  const html = await generateStaticPage(route)

  // Update cache
  cache.set(cacheKey, {
    html,
    timestamp: now,
    ttl: 10 * 60 * 1000 // 10-minute TTL
  })

  return html
}

async function generateStaticPage(route: string) {
  // Actual static page generation logic...
  return `Generated content for ${route}`
}

// Periodically clean expired cache
setInterval(() => {
  const now = Date.now()
  for (const [key, entry] of cache.entries()) {
    if (now - entry.timestamp > entry.ttl) {
      cache.delete(key)
    }
  }
}, 60 * 1000) // Check every minute

Static Asset Optimization:

// src/server/staticAssets.ts
import { serve } from 'bun'
import path from 'path'

const assetServer = serve({
  port: 3001,
  fetch(request) {
    const url = new URL(request.url)
    const filePath = path.join(process.cwd(), 'dist', url.pathname)

    return Bun.file(filePath).response
  }
})

// Enable asset optimization middleware in production
if (process.env.NODE_ENV === 'production') {
  app.use(async (ctx, next) => {
    // 1. Gzip compression
    ctx.response.headers.set('content-encoding', 'gzip')

    // 2. Cache control
    if (ctx.request.url.endsWith('.js') || ctx.request.url.endsWith('.css')) {
      ctx.response.headers.set('cache-control', 'public, max-age=31536000, immutable')
    }

    await next()
  })
}

SSG and CMS Integration

Headless CMS Data Fetching:

// src/content/cmsClient.ts
import axios from 'axios'

const CMS_API_URL = process.env.CMS_API_URL || 'https://cms.example.com/api'

export async function fetchCmsPage(slug: string) {
  try {
    const response = await axios.get(`${CMS_API_URL}/pages/${slug}`)
    return response.data
  } catch (err) {
    console.error('Failed to fetch CMS page:', err)
    throw err
  }
}

// Example CMS response structure
interface CmsPage {
  id: string
  slug: string
  title: string
  content: string // HTML or Markdown
  meta: {
    description: string
    keywords: string
  }
}

// Generate static pages based on CMS content
export async function generateCmsPages() {
  // 1. Fetch all page slugs
  const pagesResponse = await axios.get(`${CMS_API_URL}/pages`)
  const pages = pagesResponse.data

  // 2. Generate static HTML for each page
  await Promise.all(
    pages.map(async (page: CmsPage) => {
      const html = renderToString(
        <App pageData={page} />
      )

      const filePath = path.join(process.cwd(), 'dist', `${page.slug}.html`)
      await fs.mkdir(path.dirname(filePath), { recursive: true })
      await fs.writeFile(filePath, `
        <!DOCTYPE html>
        <html>
          <head>
            <title>${page.title}</title>
            <meta name="description" content="${page.meta.description}">
            <meta name="keywords" content="${page.meta.keywords}">
          </head>
          <body>
            <div id="root">${html}</div>
            <script src="/client.js"></script>
          </body>
        </html>
      `)
    })
  )
}

SSG Use Cases

Documentation Site Generation:

// src/content/docsGenerator.ts
import fs from 'fs/promises'
import path from 'path'
import { renderToString } from 'react-dom/server'
import DocsLayout from '../components/DocsLayout'
import { loadMarkdownFile } from './loadMarkdown'

export async function generateDocsSite() {
  const docsDir = path.join(process.cwd(), 'content/docs')
  const files = await fs.readdir(docsDir)

  // 1. Generate individual documentation pages
  await Promise.all(
    files.map(async (file) => {
      const filePath = path.join(docsDir, file)
      const { html: markdownHtml, frontMatter } = await loadMarkdownFile(filePath)

      const docHtml = renderToString(
        <DocsLayout content={markdownHtml} meta={frontMatter} />
      )

      const outputPath = path.join(
        process.cwd(),
        'dist',
        'docs',
        file.replace('.md', '.html')
      )
      await fs.mkdir(path.dirname(outputPath), { recursive: true })
      await fs.writeFile(outputPath, `
        <!DOCTYPE html>
        <html>
          <head>
            <title>${frontMatter.title || 'Documentation'}</title>
          </head>
          <body>
            <div id="root">${docHtml}</div>
            <script src="/client.js"></script>
          </body>
        </html>
      `)
    })
  )

  // 2. Generate documentation index page
  const docLinks = files.map(file => ({
    title: path.basename(file, '.md'),
    url: `/docs/${file.replace('.md', '')}`
  }))

  const indexHtml = renderToString(
    <DocsLayout>
      <ul>
        {docLinks.map(link => (
          <li key={link.url}>
            <a href={link.url}>{link.title}</a>
          </li>
        ))}
      </ul>
    </DocsLayout>
  )

  await fs.writeFile(
    path.join(process.cwd(), 'dist', 'docs', 'index.html'),
    `
      <!DOCTYPE html>
      <html>
        <head>
          <title>Documentation Index</title>
        </head>
        <body>
          <div id="root">${indexHtml}</div>
          <script src="/client.js"></script>
        </body>
      </html>
    `
  )
}

Blog System Implementation:

// src/content/blogGenerator.ts
import fs from 'fs/promises'
import path from 'path'
import { renderToString } from 'react-dom/server'
import BlogPost from '../components/BlogPost'
import BlogIndex from '../components/BlogIndex'
import { loadMarkdownFile } from './loadMarkdown'

export async function generateBlogSite() {
  const blogDir = path.join(process.cwd(), 'content/blog')
  const files = await fs.readdir(blogDir)

  // 1. Generate individual blog posts
  const posts = await Promise.all(
    files.map(async (file) => {
      const filePath = path.join(blogDir, file)
      const { html: markdownHtml, frontMatter } = await loadMarkdownFile(filePath)

      const postHtml = renderToString(
        <BlogPost content={markdownHtml} meta={frontMatter} />
      )

      const outputPath = path.join(
        process.cwd(),
        'dist',
        'blog',
        frontMatter.slug || file.replace('.md', '.html')
      )
      await fs.mkdir(path.dirname(outputPath), { recursive: true })
      await fs.writeFile(outputPath, `
        <!DOCTYPE html>
        <html>
          <head>
            <title>${frontMatter.title || 'Blog Post'}</title>
          </head>
          <body>
            <div id="root">${postHtml}</div>
            <script src="/client.js"></script>
          </body>
        </html>
      `)

      return {
        slug: frontMatter.slug || file.replace('.md', ''),
        title: frontMatter.title,
        date: frontMatter.date,
        excerpt: markdownHtml.substring(0, 150) + '...'
      }
    })
  )

  // 2. Sort by date
  posts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())

  // 3. Generate blog index page
  const indexHtml = renderToString(
    <BlogIndex posts={posts} />
  )

  await fs.writeFile(
    path.join(process.cwd(), 'dist', 'blog', 'index.html'),
    `
      <!DOCTYPE html>
      <html>
        <head>
          <title>Blog</title>
        </head>
        <body>
          <div id="root">${indexHtml}</div>
          <script src="/client.js"></script>
        </body>
      </html>
    `
  )

  // 4. Generate RSS feed
  const rssFeed = generateRssFeed(posts)
  await fs.writeFile(
    path.join(process.cwd(), 'dist', 'blog', 'rss.xml'),
    rssFeed
  )
}

function generateRssFeed(posts: Array<{ slug: string; title: string; date: string; excerpt: string }>) {
  return `
    <?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0">
      <channel>
        <title>Blog</title>
        <link>https://example.com/blog</link>
        <description>Latest blog posts</description>
        ${posts.map(post => `
          <item>
            <title>${post.title}</title>
            <link>https://example.com/blog/${post.slug}</link>
            <description>${post.excerpt}</description>
            <pubDate>${post.date}</pubDate>
          </item>
        `).join('')}
      </channel>
    </rss>
  `
}

Real-Time Application Development

WebSocket Real-Time Communication

Bun.js WebSocket Server Implementation:

// src/server/websocket.ts
import { serve } from 'bun'

const server = serve({
  port: 3000,
  fetch(request) {
    // Upgrade HTTP connection to WebSocket
    if (request.headers.get('upgrade')?.toLowerCase() === 'websocket') {
      const { socket, response } = Bun.upgradeWebSocket(request)

      // WebSocket event handlers
      socket.onopen = () => {
        console.log('WebSocket connected')
        socket.send(JSON.stringify({ type: 'WELCOME', message: 'Connected to Bun WebSocket' }))
      }

      socket.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data)
          handleMessage(socket, data)
        } catch (err) {
          console.error('WebSocket message error:', err)
        }
      }

      socket.onclose = () => {
        console.log('WebSocket disconnected')
      }

      socket.onerror = (error) => {
        console.error('WebSocket error:', error)
      }

      return response
    }

    // Regular HTTP request handling
    return new Response('HTTP Server', { status: 200 })
  }
})

// Message handling logic
function handleMessage(socket: WebSocket, data: any) {
  switch (data.type) {
    case 'CHAT_MESSAGE':
      // Broadcast message to all clients
      broadcastMessage({
        type: 'CHAT_MESSAGE',
        sender: data.sender,
        content: data.content,
        timestamp: Date.now()
      })
      break

    case 'TYPING_INDICATOR':
      // Send to specific room/user
      sendToRoom(data.roomId, {
        type: 'TYPING_INDICATOR',
        userId: data.userId
      })
      break
  }
}

// Broadcast message to all clients
function broadcastMessage(message: any) {
  // In real applications, maintain client list
  // Simplified example
  console.log('Broadcasting message:', message)
}

// Send message to specific room
function sendToRoom(roomId: string, message: any) {
  console.log(`Sending to room ${roomId}:`, message)
}

console.log('WebSocket Server running on ws://localhost:3000')

Client Implementation:

// src/client/websocketClient.ts
export class WebSocketClient {
  private socket: WebSocket | null = null
  private messageHandlers: Array<(data: any) => void> = []

  connect(url: string) {
    this.socket = new WebSocket(url)

    this.socket.onopen = () => {
      console.log('WebSocket connected')
    }

    this.socket.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data)
        this.messageHandlers.forEach(handler => handler(data))
      } catch (err) {
        console.error('Message parse error:', err)
      }
    }

    this.socket.onclose = () => {
      console.log('WebSocket disconnected')
      // Optional: Implement auto-reconnect
      setTimeout(() => this.connect(url), 5000)
    }

    this.socket.onerror = (error) => {
      console.error('WebSocket error:', error)
    }
  }

  send(data: any) {
    if (this.socket?.readyState === WebSocket.OPEN) {
      this.socket.send(JSON.stringify(data))
    } else {
      console.error('WebSocket not connected')
    }
  }

  onMessage(handler: (data: any) => void) {
    this.messageHandlers.push(handler)
    return () => {
      this.messageHandlers = this.messageHandlers.filter(h => h !== handler)
    }
  }
}

// Usage example
const wsClient = new WebSocketClient()
wsClient.connect('ws://localhost:3000')

wsClient.onMessage((data) => {
  if (data.type === 'CHAT_MESSAGE') {
    console.log('New message:', data.content)
  }
})

// Send message
wsClient.send({
  type: 'CHAT_MESSAGE',
  sender: 'user123',
  content: 'Hello from Bun!'
})

GraphQL and Bun.js Integration

Apollo Server Integration:

// src/server/graphql.ts
import { serve } from 'bun'
import { ApolloServer } from '@apollo/server'
import { startStandaloneServer } from '@apollo/server/standalone'
import { typeDefs } from './schema'
import { resolvers } from './resolvers'

async function startGraphQLServer() {
  const server = new ApolloServer({
    typeDefs,
    resolvers
  })

  const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 }
  })

  console.log(`GraphQL server ready at ${url}`)
}

// Run both HTTP and GraphQL servers
serve({
  port: 3000,
  fetch(request) {
    // HTTP request handling...
  }
})

startGraphQLServer()

Native GraphQL Implementation with Bun:

// src/server/nativeGraphQL.ts
import { serve } from 'bun'

// Simple GraphQL type definition
type User = {
  id: string
  name: string
  email: string
}

// Simulated database
const users: User[] = [
  { id: '1', name: 'Alice', email: 'alice@example.com' },
  { id: '2', name: 'Bob', email: 'bob@example.com' }
]

// GraphQL resolvers
const resolvers = {
  Query: {
    users: () => users,
    user: (_: any, { id }: { id: string }) => 
      users.find(user => user.id === id) || null
  },
  Mutation: {
    createUser: (_: any, { name, email }: { name: string; email: string }) => {
      const newUser = { id: Date.now().toString(), name, email }
      users.push(newUser)
      return newUser
    }
  }
}

// GraphQL schema
const typeDefs = `
  type User {
    id: ID!
    name: String!
    email: String!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
  }

  type Mutation {
    createUser(name: String!, email: String!): User!
  }
`

// Simple GraphQL request handler
async function handleGraphQLRequest(request: Request) {
  if (request.method !== 'POST') {
    return new Response('Method Not Allowed', { status: 405 })
  }

  try {
    const { query, variables } = await request.json()

    // Simplified GraphQL execution (use full parser in real projects)
    if (query.includes('users')) {
      return new Response(JSON.stringify({ data: { users } }), {
        headers: { 'Content-Type': 'application/json' }
      })
    }

    if (query.includes('user')) {
      const id = query.match(/id:\s*"([^"]+)"/)?.[1]
      const user = users.find(u => u.id === id)
      return new Response(JSON.stringify({ data: { user } }), {
        headers: { 'Content-Type': 'application/json' }
      })
    }

    if (query.includes('createUser')) {
      const name = query.match(/name:\s*"([^"]+)"/)?.[1]
      const email = query.match(/email:\s*"([^"]+)"/)?.[1]
      const newUser = resolvers.Mutation.createUser(null, { name, email })
      return new Response(JSON.stringify({ data: { createUser: newUser } }), {
        headers: { 'Content-Type': 'application/json' }
      })
    }

    return new Response(JSON.stringify({ errors: [{ message: 'Invalid query' }] }), {
      status: 400,
      headers: { 'Content-Type': 'application/json' }
    })
  } catch (err) {
    console.error('GraphQL error:', err)
    return new Response(JSON.stringify({ errors: [{ message: 'Internal Server Error' }] }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' }
    })
  }
}

const server = serve({
  port: 4000,
  fetch: handleGraphQLRequest
})

console.log('Native GraphQL Server running on http://localhost:4000')

Real-Time Collaborative Editing

CRDT Implementation Basics:

// src/collaboration/crdt.ts
type Character = {
  id: string // Unique identifier (includes site ID and counter)
  value: string
  position: number // Logical position
}

export class CRDTDocument {
  private characters: Character[] = []
  private siteId: string
  private counter: number = 0

  constructor(siteId: string) {
    this.siteId = siteId
  }

  // Generate unique character ID
  private generateId(): string {
    return `${this.siteId}-${this.counter++}`
  }

  // Local character insertion
  insert(value: string, position: number) {
    const id = this.generateId()
    const char: Character = { id, value, position }

    // Find insertion position
    let insertIndex = this.characters.findIndex(c => c.position >= position)
    if (insertIndex === -1) {
      insertIndex = this.characters.length
    }

    this.characters.splice(insertIndex, 0, char)
  }

  // Local character deletion
  delete(position: number) {
    const index = this.characters.findIndex(c => c.position === position)
    if (index !== -1) {
      this.characters.splice(index, 1)
    }
  }

  // Apply remote operation
  applyRemoteOperation(char: Character) {
    // Check if character with same ID exists
    const existingIndex = this.characters.findIndex(c => c.id === char.id)
    if (existingIndex !== -1) {
      // Update existing character
      this.characters[existingIndex] = char
    } else {
      // Insert new character
      let insertIndex = this.characters.findIndex(c => c.position >= char.position)
      if (insertIndex === -1) {
        insertIndex = this.characters.length
      }
      this.characters.splice(insertIndex, 0, char)
    }
  }

  // Get document content
  getContent(): string {
    return this.characters.map(c => c.value).join('')
  }
}

// Usage example
const doc1 = new CRDTDocument('site1')
const doc2 = new CRDTDocument('site2')

doc1.insert('H', 0)
doc1.insert('e', 1)
doc1.insert('l', 2)
doc1.insert('l', 3)
doc1.insert('o', 4)

// Simulate network transmission: sync doc1 operations to doc2
doc1.characters.forEach(char => {
  doc2.applyRemoteOperation(char)
})

console.log('Doc1 content:', doc1.getContent()) // Output: Hello
console.log('Doc2 content:', doc2.getContent()) // Output: Hello

// Concurrent editing example
// Site 1 inserts 'X' at position 1
doc1.insert('X', 1)
console.log('After doc1 insert X:', doc1.getContent()) // Output: HXello

// Site 2 inserts 'Y' at position 2
doc2.insert('Y', 2)
console.log('After doc2 insert Y:', doc2.getContent()) // Output: HeYllo

// Sync doc2 operations to doc1
doc2.characters.forEach(char => {
  // Avoid reapplying existing characters
  if (!doc1.characters.some(c => c.id === char.id)) {
    doc1.applyRemoteOperation(char)
  }
})

console.log('After sync, doc1 content:', doc1.getContent()) // Output: HXYello
console.log('After sync, doc2 content:', doc2.getContent()) // Output: HeYllo

Complete Real-Time Collaborative Editing Implementation:

// src/collaboration/realtimeEditor.ts
import { serve } from 'bun'
import { CRDTDocument } from './crdt'
import { WebSocketClient } from '../client/websocketClient'

type EditorState = {
  document: CRDTDocument
  clients: Set<WebSocketClientWrapper>
}

class RealtimeEditorServer {
  private editors: Map<string, EditorState> = new Map() // documentId -> editor state

  constructor() {
    this.startServer()
  }

  private startServer() {
    const server = serve({
      port: 3000,
      fetch: async (request) => {
        // WebSocket upgrade handling
        if (request.headers.get('upgrade')?.toLowerCase() === 'websocket') {
          const { socket, response } = Bun.upgradeWebSocket(request)
          await this.handleWebSocketConnection(socket)
          return response
        }

        // HTTP API endpoint (optional)
        return new Response('Realtime Editor Server', { status: 200 })
      }
    })

    console.log('Realtime Editor Server running on ws://localhost:3000')
  }

  private async handleWebSocketConnection(socket: WebSocket) {
    console.log('New client connected')

    // 1. Client handshake (get document ID)
    socket.onmessage = async (event) => {
      try {
        const message = JSON.parse(event.data)

        if (message.type === 'JOIN_DOCUMENT') {
          const { documentId } = message

          // Get or create editor state
          let editorState = this.editors.get(documentId)
          if (!editorState) {
            editorState = {
              document: new CRDTDocument(this.generateSiteId()),
              clients: new Set()
            }
            this.editors.set(documentId, editorState)
          }

          // 2. Add client to editor
          editorState.clients.add(new WebSocketClientWrapper(socket))

          // 3. Send current document state to new client
          const currentState = editorState.document.getContent()
          socket.send(JSON.stringify({
            type: 'DOCUMENT_STATE',
            content: currentState
          }))

          // 4. Notify other clients of new user
          this.broadcastToOthers(editorState, {
            type: 'USER_JOINED',
            userId: socket.id
          }, socket)
        }

        if (message.type === 'EDIT_OPERATION') {
          const { documentId, operation } = message
          const editorState = this.editors.get(documentId)

          if (editorState) {
            // Apply operation to CRDT document
            if (operation.type === 'INSERT') {
              editorState.document.insert(operation.value, operation.position)
            } else if (operation.type === 'DELETE') {
              editorState.document.delete(operation.position)
            }

            // Broadcast operation to other clients
            this.broadcastToOthers(editorState, message, socket)
          }
        }
      } catch (err) {
        console.error('WebSocket message error:', err)
      }
    }

    socket.onclose = () => {
      console.log('Client disconnected')
      // Clean up client references (needs improvement: find client's editor)
    }
  }

  private broadcastToOthers(editorState: EditorState, message: any, excludeSocket: WebSocket) {
    editorState.clients.forEach(client => {
      if (client.socket !== excludeSocket && client.socket.readyState === WebSocket.OPEN) {
        client.socket.send(JSON.stringify(message))
      }
    })
  }

  private generateSiteId(): string {
    return Math.random().toString(36).substring(2, 10)
  }
}

// WebSocket client wrapper (simplified)
class WebSocketClientWrapper {
  constructor(public socket: WebSocket) {}
}

// Start server
new RealtimeEditorServer()

Client-Side Collaborative Editing Integration:

// src/client/collaborativeEditor.ts
import { WebSocketClient } from './websocketClient'
import { CRDTDocument } from '../server/collaboration/crdt'

export class CollaborativeEditor {
  private document: CRDTDocument
  private wsClient: WebSocketClient
  private documentId: string

  constructor(documentId: string) {
    this.documentId = documentId
    this.document = new CRDTDocument(this.generateClientId())
    this.wsClient = new WebSocketClient()

    this.connect()
    this.setupEventListeners()
  }

  private connect() {
    this.wsClient.connect('ws://localhost:3000')

    this.wsClient.onMessage((message) => {
      if (message.type === 'DOCUMENT_STATE') {
        // Initialize local document state
        this.document = new CRDTDocument(this.generateClientId())
        // Simplified: Implement logic to rebuild CRDT document from string
        console.log('Received document state:', message.content)
      }

      if (message.type === 'EDIT_OPERATION') {
        // Apply remote operation to local document
        if (message.operation.type === 'INSERT') {
          this.document.insert(message.operation.value, message.operation.position)
        } else if (message.operation.type === 'DELETE') {
          this.document.delete(message.operation.position)
        }
        this.updateUI()
      }
    })
  }

  private setupEventListeners() {
    // Listen for local edit events (e.g., input changes)
    document.getElementById('editor')?.addEventListener('input', (e) => {
      const target = e.target as HTMLInputElement
      const newValue = target.value

      // Simplified diff calculation (use more precise algorithm in real projects)
      const oldLength = this.document.getContent().length
      const newLength = newValue.length

      if (newLength > oldLength) {
        // Insert operation
        const insertedText = newValue.substring(oldLength)
        this.document.insert(insertedText, oldLength)

        // Send operation to server
        this.wsClient.send({
          type: 'EDIT_OPERATION',
          documentId: this.documentId,
          operation: {
            type: 'INSERT',
            value: insertedText,
            position: oldLength
          }
        })
      } else if (newLength < oldLength) {
        // Delete operation
        const deletedCount = oldLength - newLength
        this.document.delete(oldLength - 1) // Simplified: delete last character

        // Send operation to server
        this.wsClient.send({
          type: 'EDIT_OPERATION',
          documentId: this.documentId,
          operation: {
            type: 'DELETE',
            position: oldLength - 1
          }
        })
      }
    })
  }

  private updateUI() {
    // Update editor UI display
    const editorElement = document.getElementById('editor') as HTMLInputElement
    if (editorElement) {
      editorElement.value = this.document.getContent()
    }
  }

  private generateClientId(): string {
    return Math.random().toString(36).substring(2, 10)
  }
}

// Usage example
new CollaborativeEditor('doc-123')

Real-Time Application Performance Optimization

WebSocket Message Compression:

// src/server/compressedWebSocket.ts
import { serve } from 'bun'
import pako from 'pako' // zlib compression library

const server = serve({
  port: 3000,
  fetch(request) {
    if (request.headers.get('upgrade')?.toLowerCase() === 'websocket') {
      const { socket, response } = Bun.upgradeWebSocket(request)

      const compressedSocket = {
        send(data: any) {
          // Compress data
          const jsonStr = JSON.stringify(data)
          const compressed = pako.deflate(jsonStr)
          const base64 = btoa(String.fromCharCode(...compressed))

          socket.send(base64)
        },

        onmessage(event) {
          // Decompress data
          const base64 = event.data
          const binaryString = atob(base64)
          const bytes = new Uint8Array(binaryString.length)
          for (let i = 0; i < binaryString.length; i++) {
            bytes[i] = binaryString.charCodeAt(i)
          }
          const decompressed = pako.inflate(bytes)
          const jsonStr = new TextDecoder().decode(decompressed)
          const data = JSON.parse(jsonStr)

          // Trigger original onmessage event
          if (socket.onmessage) {
            socket.onmessage({ data })
          }
        }
      }

      // Replace original socket methods
      socket.send = compressedSocket.send.bind(compressedSocket)

      // ...other WebSocket event handling...
    }

    return new Response('Compressed WebSocket Server', { status: 200 })
  }
})

console.log('Compressed WebSocket Server running on ws://localhost:3000')

Message Batching and Throttling:

// src/client/batchedWebSocket.ts
export class BatchedWebSocketClient {
  private socket: WebSocket
  private messageQueue: Array<{ type: string; data: any }> = []
  private batchInterval: number
  private isBatching: boolean = false

  constructor(url: string, batchInterval: number = 50) {
    this.socket = new WebSocket(url)
    this.batchInterval = batchInterval

    this.socket.onopen = () => {
      this.startBatching()
    }
  }

  private startBatching() {
    setInterval(() => {
      if (this.messageQueue.length > 0) {
        this.flushBatch()
      }
    }, this.batchInterval)
  }

  private flushBatch() {
    if (this.messageQueue.length === 0) return

    const batch = this.messageQueue.splice(0, this.messageQueue.length)
    this.socket.send(JSON.stringify({
      type: 'BATCH',
      messages: batch
    }))
  }

  send(type: string, data: any) {
    this.messageQueue.push({ type, data })

    // Flush immediately if queue reaches threshold
    if (this.messageQueue.length >= 10) {
      this.flushBatch()
    }
  }

  onMessage(handler: (data: any) => void) {
    this.socket.onmessage = (event) => {
      const message = JSON.parse(event.data)

      if (message.type === 'BATCH') {
        message.messages.forEach(msg => handler(msg))
      } else {
        handler(message)
      }
    }
  }
}

// Usage example
const client = new BatchedWebSocketClient('ws://localhost:3000')
client.send('CURSOR_MOVE', { x: 100, y: 200 })
client.send('SELECTION_CHANGE', { start: 0, end: 10 })

// Batch sending triggered automatically after 50ms interval

Real-Time Application Security and Access Control

WebSocket Authentication and Authorization:

// src/server/secureWebSocket.ts
import { serve } from 'bun'

const server = serve({
  port: 3000,
  fetch(request) {
    if (request.headers.get('upgrade')?.toLowerCase() === 'websocket') {
      // 1. HTTP-level authentication
      const authToken = request.headers.get('sec-websocket-protocol')

      if (!validateToken(authToken)) {
        return new Response('Unauthorized', { status: 401 })
      }

      // 2. Upgrade connection
      const { socket, response } = Bun.upgradeWebSocket(request, {
        protocol: authToken // Pass auth token
      })

      // 3. WebSocket-level permission control
      socket.onmessage = (event) => {
        try {
          const message = JSON.parse(event.data)

          // Check if user has permission for operation
          if (!checkPermission(authToken, message.type)) {
            socket.send(JSON.stringify({
              type: 'ERROR',
              message: 'Permission denied'
            }))
            return
          }

          // Handle message...
        } catch (err) {
          console.error('WebSocket error:', err)
        }
      }

      return response
    }

    return new Response('Secure WebSocket Server', { status: 200 })
  }
})

// Simulated authentication validation
function validateToken(token: string | null): boolean {
  return token === 'valid-token' // Use JWT or other standard in real projects
}

// Simulated permission check
function checkPermission(token: string | null, operation: string): boolean {
  if (!token) return false

  const userPermissions = {
    'valid-token': ['READ', 'WRITE'] // Example permissions
  }

  return userPermissions[token]?.includes(operation) || false
}

console.log('Secure WebSocket Server running on ws://localhost:3000')

Rate Limiting Implementation:

// src/middleware/rateLimiter.ts
import { Context, Next } from 'bun'

type RateLimitConfig = {
  windowMs: number // Time window (milliseconds)
  maxRequests: number // Maximum requests
}

const rateLimiters = new Map<string, { count: number; resetTime: number }>()

export function rateLimiter(config: RateLimitConfig) {
  return async (ctx: Context, next: Next) => {
    const clientIp = ctx.request.ip || 'unknown'
    const now = Date.now()

    if (!rateLimiters.has(clientIp)) {
      rateLimiters.set(clientIp, {
        count: 1,
        resetTime: now + config.windowMs
      })
    } else {
      const limiter = rateLimiters.get(clientIp)!

      if (now > limiter.resetTime) {
        // Reset counter
        limiter.count = 1
        limiter.resetTime = now + config.windowMs
      } else {
        // Increment counter
        limiter.count++

        if (limiter.count > config.maxRequests) {
          ctx.response.status = 429
          ctx.response.body = 'Too Many Requests'
          return
        }
      }
    }

    await next()
  }
}

// Usage example
server.use(rateLimiter({
  windowMs: 10 * 1000, // 10-second window
  maxRequests: 100     // Max 100 requests
}))

Data Validation and Sanitization:

// src/utils/sanitization.ts
import DOMPurify from 'dompurify' // HTML sanitization library

export function sanitizeInput(input: string, type: 'text' | 'html' | 'url') {
  switch (type) {
    case 'html':
      // Sanitize HTML to prevent XSS
      return DOMPurify.sanitize(input)

    case 'url':
      // Validate URL format
      try {
        new URL(input)
        return input
      } catch {
        throw new Error('Invalid URL format')
      }

    case 'text':
    default:
      // Basic text sanitization
      return input.replace(/[ {
  try {
    const message = JSON.parse(event.data)

    // Sanitize user input
    if (message.type === 'CHAT_MESSAGE') {
      message.content = sanitizeInput(message.content, 'html')
    }

    // Process sanitized message...
  } catch (err) {
    console.error('Message processing error:', err)
  }
}

Summary

Key points for Bun.js advanced application development:

  1. Server-Side Rendering (SSR):
    • Leverage Bun’s fast startup for instant page rendering
    • Use streaming rendering to improve first-paint performance
    • Implement component-level caching to reduce redundant computation
  2. Static Site Generation (SSG):
    • Best practices for build-time static file generation
    • Automation for dynamic routing and content management
    • Seamless integration with CMS systems
  3. Real-Time Application Development:
    • Hybrid WebSocket and GraphQL communication models
    • CRDT algorithms for conflict-free collaborative editing
    • Comprehensive performance optimization and security solutions

Architecture Recommendations:

  • Use SSR for SEO-sensitive pages requiring fast first paint
  • Adopt SSG with incremental regeneration for content-stable pages
  • Implement WebSocket + CRDT for high-realtime requirements
  • Use hybrid architectures to leverage the strengths of different technologies

By leveraging Bun.js’s high-performance capabilities, developers can build modern web applications that offer both excellent user experiences and high development efficiency.

Share your love