Can I use Hono with Nitro without starting a port?

HEAD
4df7aabfeat: `nitro deploy` command (#4042)
This post might have stale content, as HEAD is 60 commits ahead.

if i wanna use hono w nitro, but don't want to start an app in a port, but rather, target hono's fetch in-process: is that possible?

Avatar of anthropic/claude-opus-4.6
Claude Opus 4.6
commented

I now have a thorough understanding. Let me provide a comprehensive answer.


Yes, absolutely! This is not only possible, it's actually a first-class pattern in Nitro. Here's what's going on under the hood:

##How it works

At its core, every Nitro app exposes a fetch handler — the nitroApp.fetch function (src/runtime/internal/app.ts:153). This is a standard Web fetch-compatible function ((Request) => Response | Promise<Response>). Every Nitro preset — whether it's Cloudflare Workers, Deno Deploy, Node, etc. — ultimately just wires up nitroApp.fetch to whatever runtime interface it needs. No port is inherently required.

The standard preset does exactly what you want — it exports a bare fetch handler with no server startup:

// src/presets/standard/runtime/server.ts
import { useNitroApp } from "nitro/app";
const nitroApp = useNitroApp();

export default {
  fetch: nitroApp.fetch,
};

Build with preset: "standard" and you get a module that just exports { fetch }. You can import it and call fetch(request) directly in-process.

##Option 2: Use the node-middleware preset

If you're in a Node.js environment and want a Node-compatible handler (not a raw fetch), the node-middleware preset exports a connect/express-style middleware — also no port listening:

// src/presets/node/runtime/node-middleware.ts
const nitroApp = useNitroApp();
export const middleware = toNodeHandler(nitroApp.fetch);

##Combining with Hono as the server entry

Since Nitro v3 auto-detects server.ts and Hono's app object already has a standard .fetch() method, you can use Hono as your server entry:

// server.ts
import { Hono } from "hono";

const app = new Hono();

app.get("/", (c) => c.text("Hello, Hono with Nitro!"));

export default app; // Hono exposes .fetch() — Nitro picks it up

This server.ts is registered as a catch-all /** route handler inside Nitro (src/routing.ts:64-71). So when nitroApp.fetch(req) is called, it flows through Nitro's middleware pipeline and eventually reaches your Hono app.

##Putting it all together

// nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "standard", // or "node-middleware"
});
// server.ts
import { Hono } from "hono";

const app = new Hono();
app.get("/", (c) => c.text("Hello!"));
export default app;

After building, the output is a module that exports { fetch }. You can call it directly in-process:

import server from "./output/server/index.mjs";

const response = await server.fetch(new Request("http://localhost/"));
console.log(await response.text()); // "Hello!"

No port. No HTTP server. Just a fetch call in-process. This is the same pattern used by Cloudflare Workers, service workers, and other edge runtimes — all of which are fundamentally just fetch handlers.


END OF POST

Can I use Hono with Nitro without starting a port? — nitrojs/nitro