André Terron

Using both Express and Hono


I have mainly used Express.JS when building APIs. It’s widely used and has been around forever, but its age is showing in its interfaces and runtime support. I briefly used koa and fastify, but like Express, they are limited to Node.js.

Hono, on the other hand, supports Deno, Bun, Cloudflare Workers, Val Town, and Node.js! It also offers modern features like a fetch-compatible API, validation, OpenAPI spec generation, and RPC.

I decided to migrate Mainframe’s Api from Express to Hono. I’m doing a gradual migration, but having both working at the same time isn’t trivial. This post is my documentation on how to get it working.

1. Hono Setup

First, install the necessary Hono packages:

npm i hono @hono/node-server

Then, create the Hono endpoints:

// hono-app.ts
import { Hono } from "hono";

export const honoApp = new Hono()

honoApp.get("/healthcheck", async (c) => {
  return c.json({ success: true });
});

2. Express integration

From the @hono/node-server usage documentation, they mention the serve(app, callback) function. But if Express is already serving your API, this results in a conflict.

The solution is to use their getRequestListener(app.fetch) function:

// hono-express.ts
import express from "express"
import { getRequestListener } from "@hono/node-server";
import { honoApp } from "./hono-app"

const expresApp = express()

// This is the translation layer between Node.js APIs and the
// web fetch APIs
const honoRequestListener = getRequestListener(honoApp.fetch);

// # Express middlewares and routes
// expressApp.use(...)
// expressApp.get('/users' , ...)

// Place this middleware after all other routes but before error handling
expressApp.use(async (req, res, next) => {
  try {
    // Call the request listener with express' req and res
    await honoRequestListener(req, res);
 } catch (e) {
    next(e);
 }
});

// # Express error handler
// expressApp.use((error, req, res, next) => { console.error(error); });

expressApp.listen(3001, () => console.log(`Example app is up!`));

Done!

You can now access your Hono endpoints within your Express app:

$ curl http://localhost:8745/healthcheck
{"success":true}

3. Bonus! Accessing Express API

Since it’s common for Express middlewares to add extra fields to the req object, you might need to access them from Hono. @hono/node-server already sets those bindings, but you can get them typed like this:

import type express from "express";

// Based on import("@hono/node-server").HttpBindings
type Bindings = {
  incoming: express.Request;
  outgoing: express.Response;
};

export const honoApp = new Hono<{Bindings: Bindings}>();

Learn more