DEV Community

Yuki Nishikawa
Yuki Nishikawa

Posted on

Next.js Forgot to Design APIs. Tirne Didn’t.

Struggling to trace side effects in Next.js API Routes?
Tired of global middleware, nested folders, and 300ms cold starts?

Tirne is here to change that. It's not just a framework. It’s a declarative, fetch-native architectural DSL for building edge-first APIs.

Tirne doesn’t just run your code. It structures it.


✨ Core Philosophy

  • Declarative routes: Define your API like a schema, not scattered handlers
  • Explicit side effects: Middleware is opt-in, visible, testable
  • Edge-native speed: Designed for Bun, Workers, and zero-cold-start runtimes
  • Type-aligned logic: Middleware and handlers work seamlessly with types

🛠️ 1. Quick Start

npx create-tirne-app
✔ Choose your target environment: › Bun / Workers
✔ Project folder: … my-tirne-app

cd my-tirne-app
bun install or npm install
npm run dev or wrangler dev
Enter fullscreen mode Exit fullscreen mode

✨ Your API will be available at http://localhost:3000.

Project Structure:

  • index.ts: Entry point using fetch-compatible interface
  • package.json: Preconfigured for Bun or Workers
  • tsconfig.json: Minimal but typed setup

⚡️ 2. Performance Benchmarks

bunx autocannon -c 100 -d 10 <http://localhost:3000/>
Enter fullscreen mode Exit fullscreen mode
Metric Tirne (Bun) Next.js API Routes
❄️ Cold Start 0.02 ms ~300 ms
⚡️ First Request 0.79 ms 20-30 ms
♻️ Requests/sec 90,000+ rps 8,000-10,000 rps
📉 Avg Latency <1ms ~15ms+

🚀 Tirne is 10x faster than Next.js API Routes — and that’s before tuning.


📀 3. Hello Tirne (Structured Example)

import { Server } from "tirne";

const server = new Server([
  { method: "GET", path: "/health", handler: () => new Response("✅ OK") }
]);

export default {
  fetch: (req: Request) => server.fetch(req),
};
Enter fullscreen mode Exit fullscreen mode

▶️ Compare that to a full folder in /pages/api/health.ts and global middleware.


🔒 4. Real Auth, Architected

import { Server, json, setCookie, requireAuth } from "tirne";
import type { Route } from "tirne";

const routes: Route[] = [
  {
    method: "GET",
    path: "/login",
    handler: () => {
      const headers = new Headers();
      headers.append("Set-Cookie", setCookie("auth", "valid-token", {
        httpOnly: true,
        path: "/",
        maxAge: 3600,
      }));
      return json({ message: "Logged in" }, 200, headers);
    },
    middleware: [],
  },
  {
    method: "GET",
    path: "/private",
    handler: () => json({ message: "Secret" }),
    middleware: [requireAuth],
  },
];

const server = new Server(routes);

export default {
  fetch: (req: Request) => server.fetch(req),
};
Enter fullscreen mode Exit fullscreen mode

✅ Auth isn’t magical. It’s explicit, testable, and architectural.


❗️ 5. Built-In Error Handling

// index.ts
import type { Route } from "tirne";
import { Server, TirneError } from "tirne";

const routes: Route[] = [
  {
    method: "GET",
    path: "/",
    handler: (req) => {
      const name = new URL(req.url).searchParams.get("name");
      if (!name) {
        throw new TirneError("Missing name", {
          status: 400,
          type: "bad_request",
          expose: true,
        });
      }
      return new Response(`Hello, ${name}`);
    },
  },
];

const server = new Server(routes);

export default {
  fetch: (req: Request) => server.fetch(req),
};
Enter fullscreen mode Exit fullscreen mode
  • TirneError gives you structured error responses
  • Built-in error middleware maps exceptions to HTTP responses
  • No try/catch needed — control flow stays clean and testable

🧼 Clean APIs include clean error boundaries.


🧠 🚀 Build APIs You Can Actually Reason About

If you think API code should be structured, testable, and explicit — not scattered and magical — Tirne was made for you.

👉 ⭐ Star on GitHub — 10× faster than Next. 100× clearer.

No hidden context. No global traps.
Just architecture you can trace, test, and trust.

Star it if you’re done guessing how your middleware works.
We built Tirne because we were, too.

We don’t need bigger frameworks.
We need smaller, sharper ones.

Less framework. More logic.

Top comments (3)

Collapse
 
yukinisihikawa profile image
Yuki Nishikawa

Curious how others are thinking about API error boundaries in 2025.

Do you still wrap handlers in try/catch manually?
Or are you also shifting toward structured exceptions + middleware?

Would love to hear your approach.

Collapse
 
yukinisihikawa profile image
Yuki Nishikawa

Thanks for reading!❤️

Some comments may only be visible to logged-in visitors. Sign in to view all comments.