Blog - Yusuke Wada

Screenshot

Introducing Hono CLI


Introducing Hono CLI

Hono has provided many innovative features: a router leveraging regular expressions, lightweight server-side JSX, RPC through TypeScript types, and multi-runtime support using Web Standards. We’ve competed globally with ideas and implementation.

Today, we’re introducing the Hono CLI.

Hono CLI is a command-line interface with an entirely new concept.

It’s a CLI for humans and AI. Once installed, you can use the hono command:

hono --help

There are five subcommands:

Let’s look at each one.

hono docs

First is hono docs. You run it like this:

hono docs [path]

For path, you specify a path from the Hono website (https://hono.dev). For example, to view the ”Routing” page, you would do:

hono docs /docs/api/routing

This lets you view the documentation.

But you might be thinking, “That’s it?” Yes, that’s it. However, the key point is that Markdown is output to standard output.

Terminal

You can probably guess. This is AI-friendly. There are no HTML tags that come with fetching web pages. Since it’s standard output, AI coding agents can read it.

Next is hono search.

hono search <query>

For query, you specify a search term. For example, to find documentation about middleware:

hono search middleware

This outputs URLs and paths of relevant pages from the website in JSON format to standard output.

Terminal

This means that combined with hono docs, “AI can autonomously search and read documentation”. For example, to search for and read documentation about basic authentication, the flow would be:

There are more AI-oriented commands. Let’s move on.

hono request

The next command is hono request.

hono request [file]

The default value for file is src/index.ts, but to specify it explicitly:

hono request src/index.ts

To understand this command, first imagine Hono’s app.request() API. Using app.request(), you can send requests to a Hono app and get responses without starting a server. This is very convenient for checking behavior and testing.

Here’s code that sends a GET request to path /, with the returned Response object going into res:

const res = await app.request('/')

hono request sends the request specified on the command line to the Hono app in the file you specify, and prints the result.

hono request src/index.ts

This executes:

const res = await app.request('/')

For example, here’s a Hello World app:

// src/index.ts
import { Hono } from 'hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello World'))

export default app

Let’s run hono request on this file.

Terminal

-P specifies the path / to request. The default method is GET. The standard output prints the result in JSON format: “status code is 200, body is Hello World”. You can do more complex things. Like curl, you can use -X to specify the method and -d to specify the body.

hono request \
  -P /api/users \
  -X POST \
  -d '{"name":"Alice"}' \
  src/index.ts

This command is very convenient for humans. Previously, when testing an app, you’d start a server using Wrangler, Deno, Bun, or Node.js commands, then hit it with a web browser or curl. But with hono request, you can test your app without starting a server!

AI can use it too. This is very powerful. You’ve probably seen coding agents start servers and run curl. You’ve also seen them fail to shut down servers properly, causing port conflicts and starting multiple servers… With hono request, you don’t even need to start a server, so you can test apps very quickly, accurately, and cleanly.

And AI can combine this command with the previous two commands:

You can write this workflow as steps in AGENTS.md or CLAUDE.md. This works as-is:

# My App Development Guidelines

## Hono Development

Use the `hono` CLI for efficient development. View all commands with `hono --help`.

### Core Commands

- **`hono docs [path]`** - Browse Hono documentation
- **`hono search <query>`** - Search documentation
- **`hono request [file]`** - Test app requests without starting a server

### Quick Examples

```bash
# Search for topics
hono search middleware
hono search "getting started"

# View documentation
hono docs /docs/api/context
hono docs /docs/guides/middleware

# Test your app
hono request -P /api/users src/index.ts
hono request -P /api/users -X POST -d '{"name":"Alice"}' src/index.ts
```

### Workflow

1. Search documentation: `hono search <query>`
2. Read relevant docs: `hono docs [path]`
3. Test implementation: `hono request [file]`

Watch the demo. You can see a coding agent creating an app using the hono commands.

Demo

hono serve

So far, these have been commands for AI. Now let’s introduce commands for humans. The first is hono serve.

hono serve index.ts

This starts the Hono app in index.ts at http://localhost:7070.

?? That’s it?? Doesn’t that just remove the need for the Node.js Adapter? Can’t you do the same thing with Wrangler, Deno, or Bun?

No, there’s a --use option. Imagine app.use().

app.use(middleware)

app.use() applies middleware to the app. For example, the built-in logger middleware is used like this:

app.use(logger())

Extract just logger() from there. In hono serve, you can specify it as the value of the --use option and apply middleware without changing your app code.

hono serve --use 'logger()' src/index.ts

This way, even apps that don’t specify a logger will output logs.

Terminal

Applying this, you can even add Basic authentication without rewriting code:

hono serve \
 --use "logger()" \
 --use "basicAuth({username:'foo',password:'bar'})" \
  src/index.ts

Now, if you don’t pass an entry path like src/index.ts to this subcommand, an empty app is used.

Terminal

Accessing http://localhost:7070 returns 404. Applying this, you can start a server combining middleware even without local files.

For example, you can start a server that serves local files:

hono serve \
 --use "serveStatic({root:'./'})"

You can also use helpers, so using the Proxy helper’s proxy, you can set up a reverse proxy for the Ramen API:

hono serve \
 --use '(c) => proxy(`https://ramen-api.dev${new URL(c.req.url).pathname}`)'

hono optimize

The final command is hono optimize.

hono optimize [entry]

Before explaining the command, let’s talk about Hono’s routers. RegExpRouter is one of the fastest routers in the JavaScript world. Under certain conditions, it’s the fastest. The idea behind RegExpRouter is to combine dynamic route information into one large regular expression and match it all at once when a request comes in. This has weaknesses:

  1. Slow initialization
  2. Large file size

To solve these, two routers were invented in Hono and provided as presets:

  1. LinearRouter - hono/quick
  2. PatternRouter - hono/tiny

Indeed, LinearRouter is fast in benchmarks including initialization, and PatternRouter has a small bundle size. But I want to use RegExpRouter!

So today we’re introducing the seventh router (the six existing ones are the five current routers plus the deprecated StaticRouter): PreparedRegExpRouter.

The idea behind PreparedRegExpRouter is to pre-convert the route information equivalent to the regular expressions created by RegExpRouter into strings and hardcode them.

Suppose you have an app like this:

import { Hono } from 'hono'

const app = new Hono()

app.get('/posts', (c) => c.json({}))
app.get('/posts/:id', (c) => c.json({}))
app.put('/posts', (c) => c.json({}))

export default app

This route information is pre-converted to strings and passed to the router’s constructor:

const routerParams = [
  { ALL: [/^\/posts\/([^/]+)$()/, [0, 0, []], { '/posts': [[], []] }] },
  { '/posts': [[['']]], '/posts/:id': [[[2], { id: 1 }]] }
]
const router = new PreparedRegExpRouter(...routerParams)

This first makes initialization faster. Let’s look at benchmark results measuring router performance including initialization.

Benchmark

If we sort this…

Benchmark

LinearRouter is naturally first, but PreparedRegExpRouter takes second place. RegExpRouter is much further back. Compared to RegExpRouter, PreparedRegExpRouter is 16.5 times faster (in benchmarks including initialization).

Back to the Hono CLI story.

hono optimize “optimizes Hono for your app”. So what is optimized Hono? It’s this:

class Hono extends HonoBase {
  constructor(options = {}) {
    super(options)
    const routerParams = [
      { ALL: [/^\/posts\/([^/]+)$()/, [0, 0, []], { '/posts': [[], []] }] },
      { '/posts': [[['']]], '/posts/:id': [[[2], { id: 1 }]] }
    ]
    this.router = new PreparedRegExpRouter(...routerParams)
  }
}

In other words, it becomes a Hono with PreparedRegExpRouter implemented for that specific app.

Let’s run hono optimize.

Terminal

The optimized result of src/index.ts is written to dist/index.js. You can then deploy it directly with wrangler deploy:

hono optimize src/index.ts
wrangler deploy dist/index.js

So how much smaller did it get? Let’s compare a build without optimization that’s minified, a file run with hono optimize with the minify option, and one using the hono/tiny preset.

Compare

While it doesn’t reach hono/tiny’s size, compared to the original app’s 18.0KB, it’s significantly smaller at 11.14KB. That’s a 38% reduction.

In other words, hono optimize makes your app faster and smaller. This is how Hono CLI provides simple subcommands for humans: hono serve and hono optimize.

Note: We want to make the optimization technique using PreparedRegExpRouter available in build and bundle tools like Vite build plugins in the future.

Summary

Hono CLI provides five subcommands:

  1. hono docs
  2. hono search
  3. hono request
  4. hono serve
  5. hono optimize

These are commands for humans and AI.

The Hono CLI repository is public:

https://github.com/honojs/cli

And you can try Hono CLI right now:

npm i @hono/cli

Enjoy!