@pegasusheavy/ngx-bun
v0.1.0
Published
Bun SSR/SSG adapter for Angular 19+ applications
Maintainers
Readme
@pegasusheavy/ngx-bun
Features
- 🚀 Blazing Fast - Leverages Bun's high-performance HTTP server and runtime
- 📦 Full SSR Support - Complete server-side rendering for Angular 19+
- 🏗️ SSG/Prerendering - Generate static HTML files at build time
- 🔄 Automatic Hydration - Seamless client-side hydration
- 📁 Static File Serving - Optimized static asset serving with compression
- 💾 Built-in Caching - LRU cache for rendered pages
- 🛠️ Angular CLI Integration - Schematics for easy setup with
ng add - 🔧 Custom Builders - Angular CLI builders for development and production
Requirements
- Angular 19.0.0 or higher
- Bun 1.0.0 or higher
- Node.js 20.0.0 or higher (for Angular CLI)
Installation
Using Angular CLI (Recommended)
ng add @pegasusheavy/ngx-bunThis will:
- Install the package and dependencies
- Generate the server file (
server.ts) - Create server-side configuration files
- Update
angular.jsonwith SSR targets - Add npm scripts for development and production
Manual Installation
# Install the package
pnpm add @pegasusheavy/ngx-bun
# Install peer dependencies
pnpm add @angular/ssr @angular/platform-serverQuick Start
1. Add SSR to your Angular project
ng add @pegasusheavy/ngx-bun2. Start the development server
pnpm dev:ssr3. Build for production
pnpm build:ssr4. Serve production build
pnpm serve:ssrConfiguration
Server Configuration
The generated server.ts file can be customized:
import { createBunServer, createBunAngularEngine } from '@pegasusheavy/ngx-bun';
const engine = createBunAngularEngine({
bootstrap: () => import('./src/main.server'),
browserDistFolder: './dist/my-app/browser',
// Cache configuration
enableCache: true,
maxCacheSize: 100,
cacheTtl: 300_000, // 5 minutes
});
createBunServer({
engine,
port: 4000,
hostname: 'localhost',
// Enable request logging
logging: true,
// Routes to skip SSR (client-only)
clientOnlyRoutes: ['/admin/*'],
// Development mode
development: process.env.NODE_ENV !== 'production',
});Route Rendering Configuration
Configure how each route is rendered in app.routes.server.ts:
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
// Client-side only (no SSR)
{ path: 'admin/**', renderMode: RenderMode.Client },
// Prerendered at build time (SSG)
{ path: 'about', renderMode: RenderMode.Prerender },
{ path: 'blog/:slug', renderMode: RenderMode.Prerender },
// Server-side rendered on each request (SSR)
{ path: '**', renderMode: RenderMode.Server },
];Angular.json Targets
The schematic adds these targets to your angular.json:
{
"serve-ssr": {
"builder": "@pegasusheavy/ngx-bun:serve",
"options": {
"browserTarget": "my-app:build",
"port": 4000
}
},
"prerender": {
"builder": "@pegasusheavy/ngx-bun:prerender",
"options": {
"browserTarget": "my-app:build:production",
"routes": ["/", "/about", "/contact"]
}
}
}API
createBunAngularEngine(options)
Creates the Angular SSR engine.
interface BunAngularEngineOptions {
// Angular bootstrap function
bootstrap: () => Promise<ApplicationRef>;
// Path to browser distribution
browserDistFolder: string;
// Path to server distribution (optional)
serverDistFolder?: string;
// Custom index.html path (optional)
indexHtml?: string;
// Additional providers (optional)
providers?: Provider[];
// Enable render caching (default: true)
enableCache?: boolean;
// Max cache entries (default: 100)
maxCacheSize?: number;
// Cache TTL in ms (default: 300000)
cacheTtl?: number;
}createBunServer(options)
Creates and starts the Bun server.
interface BunServerOptions {
// The Angular engine instance
engine: BunAngularEngine;
// Server port (default: 4000)
port?: number;
// Server hostname (default: 'localhost')
hostname?: string;
// Enable static file serving (default: true)
serveStatic?: boolean;
// Static files directory
staticDir?: string;
// Development mode (default: false)
development?: boolean;
// Enable request logging (default: false)
logging?: boolean;
// Routes to skip SSR
clientOnlyRoutes?: string[];
// TLS configuration for HTTPS
tls?: { cert: string; key: string };
// Server start callback
onStart?: (server: Server) => void;
}createRequestHandler(options)
Creates a request handler for custom server setups.
import { createRequestHandler } from '@pegasusheavy/ngx-bun';
const handler = createRequestHandler({
engine,
staticHandler,
logging: true,
});
// Use with Bun.serve directly
Bun.serve({
fetch: handler,
port: 4000,
});createStaticFileHandler(options)
Creates a static file handler with caching and compression.
interface StaticFileOptions {
// Root directory for static files
root: string;
// Index file name (default: 'index.html')
index?: string;
// Enable compression (default: true)
compression?: boolean;
// Custom MIME types
mimeTypes?: Record<string, string>;
// Cache control header
cacheControl?: string | ((path: string) => string);
}Prerendering (SSG)
Using Angular CLI
ng run my-app:prerenderProgrammatic API
import { prerenderRoutes } from '@pegasusheavy/ngx-bun/prerender';
const result = await prerenderRoutes({
engineOptions: {
bootstrap,
browserDistFolder: './dist/browser',
},
routes: ['/', '/about', '/blog/post-1', '/blog/post-2'],
outputDir: './dist/prerendered',
concurrency: 5,
generateSitemap: true,
baseUrl: 'https://example.com',
onProgress: (completed, total, path) => {
console.log(`[${completed}/${total}] ${path}`);
},
});
console.log(`Prerendered ${result.success} routes in ${result.totalTime}ms`);Dynamic Route Parameters
const result = await prerenderRoutes({
routes: [
'/',
'/about',
{
path: '/blog/:slug',
getParams: async () => {
// Fetch slugs from CMS or database
const posts = await fetchBlogPosts();
return posts.map(post => ({ slug: post.slug }));
},
},
],
// ...
});Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| NODE_ENV | Environment mode | development |
| PORT | Server port | 4000 |
| HOST | Server hostname | localhost |
| BROWSER_DIST_FOLDER | Browser dist path | Auto-detected |
| SERVER_DIST_FOLDER | Server dist path | Auto-detected |
Performance Tips
- Enable caching in production for frequently accessed pages
- Use prerendering for static content that doesn't change often
- Set appropriate cache headers for static assets
- Use
clientOnlyRoutesfor pages that don't benefit from SSR - Monitor render times using the
X-Render-Timeresponse header
Comparison with Express-based SSR
| Feature | @pegasusheavy/ngx-bun | Express SSR | |---------|-------------------|-------------| | Cold Start | ~50ms | ~200ms | | Memory Usage | Lower | Higher | | Requests/sec | Higher | Lower | | Bundle Size | Smaller | Larger | | Native TypeScript | ✅ | ❌ |
Troubleshooting
"Could not find index.html"
Make sure you've built the browser application first:
ng build"Server bootstrap not found"
Ensure main.server.ts exists and exports a bootstrap function:
// src/main.server.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';
const bootstrap = () => bootstrapApplication(AppComponent, config);
export default bootstrap;Hydration Mismatch Errors
Ensure your components handle server/browser differences:
import { isPlatformBrowser } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';
@Component({...})
export class MyComponent {
private platformId = inject(PLATFORM_ID);
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
// Browser-only code
}
}
}Contributing
Contributions are welcome! Please read our Contributing Guide for details.
License
MIT © Pegasus Heavy Industries
