or content-heavy applications, this stack removes the traditional trade-off between development convenience and production performance.
Core Solution
The implementation strategy prioritizes zero-config execution, strict type safety, and minimal abstraction layers. We will build a TypeScript API service that initializes a Cosmic content client, defines HTTP routes using Bun’s native server, and streams JSON responses directly to the edge.
Step 1: Project Initialization and Dependency Resolution
Bun’s init command scaffolds a production-ready structure without requiring manual tsconfig.json tuning or plugin configuration.
bun init --yes
bun add @cosmicjs/sdk
The SDK installs in approximately 200ms on a warmed global store. Bun’s package manager uses a content-addressable cache, eliminating duplicate downloads and symlink overhead.
Step 2: Environment Validation and Client Instantiation
Hardcoding credentials or relying on silent undefined values causes runtime failures in production. We enforce strict environment validation at startup and instantiate the content gateway.
// src/gateway/content-client.ts
import { createBucketClient } from '@cosmicjs/sdk'
export interface ContentConfig {
bucketSlug: string
readKey: string
}
function validateEnvironment(): ContentConfig {
const bucketSlug = process.env.COSMIC_BUCKET_SLUG
const readKey = process.env.COSMIC_READ_KEY
if (!bucketSlug || !readKey) {
throw new Error('Missing required Cosmic environment variables')
}
return { bucketSlug, readKey }
}
const config = validateEnvironment()
export const contentGateway = createBucketClient({
bucketSlug: config.bucketSlug,
readKey: config.readKey,
})
Architecture Rationale: Validation occurs at module load time, failing fast before the HTTP server binds. This prevents silent degradation where routes return 500 Internal Server Error due to missing credentials. The client is exported as a singleton, avoiding repeated SDK initialization overhead.
Step 3: Route Definition and Data Fetching
Bun’s native HTTP server uses a declarative routing structure that compiles to highly optimized Zig-backed request handlers. We define a versioned API endpoint that queries specific content types and restricts returned fields to minimize payload size.
// src/server.ts
import { contentGateway } from './gateway/content-client'
const server = Bun.serve({
port: Number(process.env.PORT) || 3000,
routes: {
'/api/v1/documents': async (req: Request) => {
const url = new URL(req.url)
const contentType = url.searchParams.get('type') || 'pages'
try {
const response = await contentGateway.objects
.find({ type: contentType })
.props(['title', 'slug', 'metadata', 'created'])
.limit(20)
return Response.json({
status: 'success',
data: response.objects,
total: response.total
})
} catch (error) {
console.error('Content fetch failed:', error)
return new Response(
JSON.stringify({ status: 'error', message: 'Content retrieval failed' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
)
}
},
},
fetch(req: Request) {
return new Response('Not Found', { status: 404 })
}
})
console.log(`Content API listening on port ${server.port}`)
Architecture Rationale:
Bun.serve routes are compiled at startup, avoiding middleware chain evaluation on every request.
.props() restricts the Cosmic SDK to fetch only necessary fields, reducing network transfer and JSON parsing overhead.
- Error handling is explicit and returns structured JSON, maintaining API contract consistency.
- The fallback
fetch handler ensures unmatched routes return 404 instead of hanging.
Step 4: Execution and Validation
Bun transpiles TypeScript natively at runtime. No separate compilation step is required.
bun run src/server.ts
On modern hardware, the process binds to the port in ~5ms. The first request to /api/v1/documents triggers a CDN-backed fetch from Cosmic, returning cached content in under 100ms. Subsequent requests hit the runtime memory cache or CDN edge, maintaining sub-50ms response times.
Pitfall Guide
1. Assuming Full Node.js Compatibility
Explanation: While Bun supports most Node.js APIs, native C++ addons and certain fs/net behaviors differ. Silent failures occur when packages rely on undocumented Node internals.
Fix: Run bun test early in development. Use node: prefixed imports for built-ins. Configure bunfig.toml to polyfill missing modules or fallback to --bun flag for specific packages.
2. Over-Fetching Content Objects
Explanation: The Cosmic SDK returns full object payloads by default, including large metadata trees, media URLs, and revision history. This inflates response sizes and increases JSON parsing time.
Fix: Always chain .props() with explicit field names. Use .limit() and .skip() for pagination. Monitor payload sizes in network devtools and adjust field selection accordingly.
3. Misconfiguring the Global Store
Explanation: Bun’s 7x faster warm reinstalls depend on a properly configured global cache directory. Default behavior may store caches in temporary directories that get cleared by OS cleanup utilities.
Fix: Set BUN_INSTALL_GLOBAL_DIR and BUN_INSTALL_CACHE_DIR in your environment or .env file. Verify cache persistence across CI runs and local restarts.
4. HTTP/3 Fallback Misconfiguration
Explanation: Bun v1.3 supports HTTP/3 (QUIC), but many clients, proxies, and load balancers still expect HTTP/1.1 or HTTP/2. Forcing HTTP/3 without fallback causes connection timeouts in restricted networks.
Fix: Use Bun.serve with explicit protocol negotiation or deploy behind a reverse proxy that handles QUIC termination. Test connectivity across corporate firewalls and mobile networks before production rollout.
5. Defeating Tree-Shaking with Namespace Imports
Explanation: Importing import * as Cosmic from '@cosmicjs/sdk' pulls the entire module graph into the bundle, negating Bun’s built-in optimization and increasing memory footprint.
Fix: Use named imports: import { createBucketClient } from '@cosmicjs/sdk'. Verify bundle size with bun build --target=browser --outdir=dist to confirm dead code elimination.
6. Blocking the Event Loop with Synchronous SDK Calls
Explanation: Bun provides synchronous APIs for performance, but using them for network requests or heavy JSON parsing blocks the single-threaded event loop, degrading concurrent request handling.
Fix: Always use async/await with the Cosmic SDK. Reserve synchronous methods for local file reads or configuration parsing where blocking is acceptable.
7. Ignoring Read-Only Environment Constraints
Explanation: Cosmic read keys are safe for client-side exposure, but write keys or admin tokens must never reach the browser. Accidental exposure occurs when environment variables are bundled into frontend assets.
Fix: Separate server and client builds. Use bun build with --define to strip server-only variables. Validate environment scope at runtime using process.env.NODE_ENV checks.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Static documentation site | Bun + Cosmic SDK + static export | Zero runtime overhead, CDN caching handles all traffic | Near-zero compute costs |
| Dynamic API with frequent updates | Bun HTTP server + Cosmic REST SDK | Native TS transpilation + sub-100ms CDN fetches | Low edge compute, predictable bandwidth |
| Multi-tenant SaaS with auth | Bun + Cosmic + external auth provider | Bun’s fast startup scales to serverless, Cosmic handles content isolation | Moderate compute, high scalability |
| Legacy Node.js migration | Gradual replacement using bun run | Drop-in compatibility reduces refactoring risk | Minimal migration cost, immediate performance gain |
Configuration Template
# bunfig.toml
[install]
globalDir = "./.bun/global"
cacheDir = "./.bun/cache"
[run]
bun = true
[test]
coverage = true
# .env.example
COSMIC_BUCKET_SLUG=your_bucket_slug
COSMIC_READ_KEY=your_read_key
PORT=3000
NODE_ENV=production
// tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Quick Start Guide
- Install Bun:
curl -fsSL https://bun.sh/install | bash
- Scaffold project:
bun init --yes
- Add SDK:
bun add @cosmicjs/sdk
- Create
src/server.ts with the route and client initialization code from the Core Solution section
- Set environment variables in
.env, then run: bun run src/server.ts
The server binds in ~5ms. Navigate to http://localhost:3000/api/v1/documents?type=pages to verify CDN-backed content delivery. Iterate with bun run --watch src/server.ts for instant hot-reload cycles.