I Over-Engineered My First Project: Bridging TypeScript and Zig with Bun! 🚀

Published: (March 15, 2026 at 10:29 PM EDT)
4 min read
Source: Dev.to

Source: Dev.to

Hello, hello! It’s me Owen again!

I’ve been inactive for a while and thought maybe I should post again. Some people say that coding projects should start with to‑do lists, REST APIs, generators, etc. Instead, I dove straight into the deep end: I wanted to build something fast, learn about memory management, and figure out how different languages talk to each other.

So, I built Dunena—a high‑performance, hybrid‑architecture monorepo. It leverages Bun and TypeScript for the web layer, and delegates heavy CPU tasks to Zig via a Foreign Function Interface (FFI). I also deployed it on Kubernetes (still learning Docker and K8s).

🔗 Dunena Repository on GitHub
🔗 Documentation

What is Dunena?

At its core, Dunena is a backend platform designed to handle requests quickly and efficiently. It includes routing, WebSockets, pub/sub services, and a caching layer.

The real magic is under the hood: I wanted the rapid development speed of TypeScript, but I didn’t want Node/Bun to get bogged down by heavy computations like compression, bloom filters, or complex statistical calculations.

Tech Stack

  • Runtime & Monorepo Manager: Bun 🥟
  • Primary Language: TypeScript (strict mode)
  • High‑Performance Core: Zig ⚡
  • Database: SQLite 🗄️
  • Deployment: Docker & Kubernetes 🐳

The Architecture

The coolest (and scariest) part of this project is the FFI bridge. Here’s how a request travels from TypeScript to near‑bare‑metal execution in Zig.

Zig side

// zig/src/exports.zig
const std = @import("std");

// Export a function so Bun can read it via the C ABI
export fn compute_heavy_stats(input_val: i32) i32 {
    // Imagine some incredibly complex, CPU‑blocking math here
    var result = input_val * 42;
    return result;
}

TypeScript side

// packages/platform/src/bridge/ffi.ts
import { dlopen, FFIType, suffix } from "bun:ffi";

// Load the compiled Zig library
const path = `../../zig/zig-out/lib/libdunena_core.${suffix}`;

const { symbols } = dlopen(path, {
  compute_heavy_stats: {
    args: [FFIType.i32],
    returns: FFIType.i32,
  },
});

// Now I can call Zig directly from TypeScript!
export function runStats(input: number): number {
  return symbols.compute_heavy_stats(input);
}

When a client hits the Bun server (apps/server/src/index.ts), the platform handles API routing, hands the heavy computation off to Zig, and returns the result instantly.

The “AI” Elephant in the Room

I used AI coding agents (Claude, Gemini) to speed up boilerplate, set up the monorepo structure, and scaffold the initial file architecture. The AI helped me move fast, but I still had to do the brutal, hair‑pulling debugging and fixing:

  • If the AI changed a data type in Zig but forgot to update the corresponding FFI definition in TypeScript, the server would crash with a memory‑access violation.
  • Managing pointers, preventing memory leaks, and getting Kubernetes to work with an attached SQLite volume required careful human oversight.

The experience taught me not to blindly trust generated code, especially when dealing with low‑level systems.

The Biggest Challenges

1. Managing Memory Across the Void

Passing data between TypeScript and Zig has no safety nets. Bun’s garbage collector knows nothing about Zig’s memory management, so I had to learn manual allocation and defer statements in Zig.

2. SQLite in Kubernetes

Deploying SQLite (a file‑based database) on a K8s pod via Persistent Volume Claims works for a single pod, but scaling horizontally can lead to database locking issues.

What’s Next?

The project is far from “finished.” I’m maintaining it solo but would love contributions. Future ideas include:

  • Improving the FFI layer with safer abstractions.
  • Exploring alternative storage solutions for better horizontal scaling.
  • Adding more high‑performance Zig modules (e.g., image processing, cryptography).

Let’s Connect!

I’d love any feedback, code reviews, or advice from the community.

🔗 GitHub repository
🔗 Documentation

Have you ever mixed languages like this or wrestled with FFI boundaries? Let me know in the comments! 👇

0 views
Back to Blog

Related posts

Read more »

Travigo

Travel as fast as you speak with Gemini! Where live agents meet immersive storytelling & 3D navigation. This project was created for entering the Gemini Live Ag...

Micro games

Hey Gamers! 👾 As part of the Rapid Games Prototyping module, we are tasked with reviewing a peer's game. The challenge is to analyse a prototype built in just...