Make itty-router work with Hono-style adapter pattern. Get ENV in Bun, Deno, Node.js, Cloudflare Workers/Pages.
This library ports Hono's adapter utilities to work with itty-router. A critical patch was made to correctly return environment bindings for Cloudflare Workers:
// Original Hono (incorrect for itty-adapter):
workerd: () => c.env,
// Patched for itty-adapter:
workerd: () => c as unknown as T,See src/helper/adapter/index.ts for the complete implementation.
Sync with Hono
- run
diff.shto download source from hono github, generate diff file, review with vscode
PROJECT_ROOT=$(pwd) ./scripts/diff.snpm run build
npm pack
npm i git+https://github.com/waxz/itty-adapter.git#main
# or
npm i github:waxz/itty-adapter#main
npx create-itty
- Define
bininpackage.json - Include resource
my-itty-app - Add
.npmignoreto ignore files.
"bin": {
"create-itty": "./bin/cli.js"
},
"files": [
"dist",
"README.md",
"my-itty-app"
],
To ensure your project automatically builds and includes the dist folder when installed directly via a GitHub URL (npm i github:waxz/itty-adapter#main), you need to use the prepare script in your package.json. The Solution: Use the prepare Script When you install a package from a Git repository, npm looks for a prepare script. It runs this script after the package is fetched but before it is linked into the user's node_modules.
"scripts": {
"build": "node -e \"fs.rmSync('./dist', { recursive: true, force: true })\" && tsc",
"prepare": "npm run build",
"prepack": "npm run build",
"pack": "npm pack",
"test": "cd examples && deno test tests/integration.test.ts --allow-env --allow-run --allow-write --allow-net"
},
cd examples
export NAME=world
deno task start
# Visit http://localhost:8000 -> "hello world"deno task build
NAME=world node dist/main_node.mjs
# Visit http://localhost:8000 -> "hello world"deno task build
NAME=world bun dist/main_bun.mjs
# Visit http://localhost:8000 -> "hello world"deno task build
mkdir -p cf
cp ./dist/main_cloudflare-workers.mjs ./cf/_worker.js
# Development
npx wrangler dev ./cf/_worker.js --name itty-test --compatibility-date 2025-10-04
# Deploy
npx wrangler deploy ./cf/_worker.js --name itty-test --compatibility-date 2025-10-04
# Pages
npx wrangler pages deploy ./cf --project-name itty-testnpm run pack
npm install itty-adapter-1.0.1.tgz
Returns environment variables. Runtime is auto-detected if not provided:
bun/node/edge-light: usesprocess.envdeno: usesDeno.env.toObject()workerd(Cloudflare): uses context.env directly as bindingsfastly: returns empty object
Returns: 'node' | 'deno' | 'bun' | 'workerd' | 'fastly' | 'edge-light' | 'other'
Hono-compatible context with methods:
req,env,res,error,vartext(),json(),html(),body()header(),status(),redirect(),notFound()
npm test # Run integration tests (Deno test runner)
npm run build # TypeScript compilation
bash examples/run.sh # Full server+client tests/workspaces/itty-adapter/
├── src/
│ ├── index.ts # Main exports
│ ├── context.ts # Hono Context class (ported)
│ ├── helper/adapter/
│ │ └── index.ts # env() and getRuntimeKey()
│ ├── adapter/
│ │ ├── types.ts # Type definitions
│ │ └── log.ts # Logger utilities
│ └── utils/ # HTML, MIME, headers, etc.
├── examples/
│ ├── src/app.ts # Example itty-router app
│ ├── main_*.ts # Entry points for each runtime
│ ├── tests/
│ │ ├── integration.test.ts # Integration tests
│ │ └── run.sh # Bash test runner
│ └── deno.jsonc # Deno config
├── dist/ # Compiled output
├── package.json
├── README.md
└── AGENTS.md # Development guidance
Use
@whatwg-node/serverinstead of@hono/node-server.
When using
@hono/node-server, logging the raw Request object withconsole.log(req)will freeze the server.
Root cause: @hono/node-server wraps the Web Request with custom property getters (using Object.defineProperty). When Node.js's console.log inspects the object, it accesses these getters which can cause an infinite loop or deadlock.
Evidence:
console.log(req)- freezes ❌console.log("req", req)- freezes ❌JSON.stringify(req)- works but returns{}✅console.log(req.url, req.method)- works ✅
Solution: Always extract primitive values before logging:
// ❌ Bad - freezes in Node.js
app.get("/", (req, ctx) => {
console.log("request:", req) // Freezes!
})
// ✅ Good - works in all runtimes
app.get("/", (req, ctx) => {
console.log("url:", req.url)
console.log("method:", req.method)
})