# Compare Express 5 and Hono for an Express Project

Yes. An agent ports one module of your Express app to Express 5 and to Hono in a sandbox, runs tests and a benchmark on each, and writes up the difference.

[Run this on Rush](https://cloud.getrush.ai/run/compare-libraries/express/).

## What the agent does

The agent clones the repository into its own sandbox and reads what a port would have to carry. In an Express app like gothinkster/node-express-realworld-example-app, `package.json` pins `express` at `~4.18.1` with `body-parser`, `cors` and `express-jwt`, Prisma 4 against Postgres (`src/prisma/schema.prisma`), Jest 29 with ts-jest, and Nx 17 driving `build`, `serve` and `test`. The module the prompt names is the article routes: `src/app/routes/article/article.controller.ts` registers 11 handlers on one `Router`, from `GET /articles` with `offset`, `limit`, `tag`, `author` and `favorited` query parameters to the comment and favorite routes, and calls into the 652-line `article.service.ts`.

Before porting anything it records a baseline on the default branch. It starts Postgres in the sandbox, applies the migrations in `src/prisma/migrations`, runs the seed from `src/prisma/seed.ts`, and runs `npx nx test`. That suite matters for the comparison: the Jest tests in `src/tests/services/` replace Prisma with `jest-mock-extended` and test the service functions, so they never touch the router. A port that breaks routing would still pass them. So the agent writes one set of HTTP tests against the running server for the article routes (list, feed, get by slug, create, update, delete, favorite, comments), runs them on the baseline, and uses the same file on both branches. It also notes that `e2e/src/server/server.spec.ts` expects `{ message: 'Hello API' }` while `src/main.ts` answers `{ status: 'API is running on /api' }`, so that test fails before any change and counts against neither port.

The Express 5 branch is the smaller change, and the agent lists each edit it needed. Express 5 forwards a rejected promise from an async handler to the error middleware, so the `try { … } catch (error) { next(error) }` blocks in every handler can go. `req.query` becomes a getter, path syntax moves to path-to-regexp 8, and `express-jwt` must still produce the `UnauthorizedError` that `src/main.ts` turns into a 401. The Hono branch rewrites the controller as a Hono app mounted at `/api`, swaps `cors` and `body-parser` for Hono's own middleware, and replaces `express-jwt` with a JWT check that keeps `getTokenFromHeaders` in `src/app/routes/auth/auth.ts` working, because that helper accepts both `Token` and `Bearer` prefixes and RealWorld clients send `Token`. Since only the article routes move, the Hono branch serves them beside the remaining Express routers, and the agent says how it joined the two.

Then it measures the same things on the baseline and both branches: the shared HTTP tests, `npx nx build`, lines changed from `git diff --stat`, and a short load test with a tool such as autocannon against `GET /api/articles` and one authenticated route, with the same seeded data, connection count and duration on each. Each number in the report is from a command in the run's transcript. Both branches stay in the sandbox; nothing is pushed.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Clone https://github.com/gothinkster/node-express-realworld-example-app. Start Postgres in the sandbox, apply the Prisma migrations, run the seed, and record a baseline with npx nx test and npx nx build. Write one set of HTTP tests for the article routes in src/app/routes/article/article.controller.ts that runs against the live server, and run it on the baseline. On two throwaway branches, port the article routes to Express 5 and to Hono, keeping the Token and Bearer prefixes that src/app/routes/auth/auth.ts accepts and the 401 that src/main.ts returns. Run the HTTP tests, the Jest suite, the build and the same autocannon benchmark against GET /api/articles and one authenticated route on the baseline and each branch. Write comparison.md with the diff size, test results, build result, timings, every change each port needed and the trade-offs you hit. Note any test that already fails on the baseline. Open no pull request and push nothing."
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is `comparison.md`, a file on the session with a download link that works for an hour. It has a table with one column each for the baseline, Express 5 and Hono, and rows for lines changed, files touched, the HTTP test results, the Jest results, the build result, and the benchmark's requests per second and latency percentiles. Under the table, each port has a list of the changes it needed and why, so the size of the diff can be read as work rather than as a number.

The trade-offs section is where the run earns its keep: what each option did to error handling, authentication and middleware in this codebase, which dependencies each one dropped or needed, and anything the agent had to work around. The session's transcript keeps every command it ran, including each benchmark with its full output, so the numbers can be checked or re-run.

Before deciding, a reviewer checks that the benchmark settings were the same across the three columns, that the HTTP tests covered the routes the team cares about, and that the Hono branch's mixing with Express is acceptable as a temporary state or would need the rest of the routers ported too. To take the next step, send the same session a follow-up asking it to open a pull request from the branch you chose; the sandbox wakes with both branches still on its disk.

## Run it on every framework major release

A comparison like this goes stale when either side ships a major version. Have a job on your server watch the release feeds you care about and send this request when one appears, with an `Idempotency-Key` built from the version so one release starts one run.

Set a webhook URL on the API key, and Rush POSTs `run.completed` with the `session_url` when the run finishes. Your server attaches the new `comparison.md` to the architecture decision it informs, so the record shows how the numbers moved between releases.

## Why run it on Rush?

A fair spike needs a database, a seeded dataset, three checkouts and a benchmark that is not competing with an editor, a browser and a dev server. In a Rush sandbox the agent gets a machine of its own for it: Postgres, the seed, both branches and the load tests run there, and nothing on your laptop changes.

What makes the write-up trustworthy is that the claims come with their commands. Every row in the table points back to a step in the transcript with its output, so the comparison can be argued about on the evidence. You can run the same request on several services at once, one run each, up to the key's concurrent run limit, and compare the reports side by side.

## Frequently asked questions

### Will the agent change my repository?

No. The prompt clones the repository into the sandbox, does the work on throwaway branches there, and says to push nothing and open no pull request. The result is a report you download.

### Why port only one module instead of the whole app?

One module with real routing, authentication and database access shows the shape of the migration and gives a like-for-like benchmark, at a fraction of the work of a full port. The report says what the rest of the app would add, such as the other routers and middleware still on Express.

### Are the benchmark numbers meaningful?

They compare the three versions on the same sandbox, data and settings, so the relative difference is the useful part. They are not production numbers: the sandbox is not your production hardware, and the report gives the exact command so you can repeat it where it matters.

### Can I compare Express 5 with Fastify instead?

Yes. Change the second target in the prompt; the baseline, the shared HTTP tests and the measurements stay the same, and the report gets a Fastify column instead of a Hono one.
