Express middleware for submounting Hono app
Which middleware is the feature for?
@hono/express
What is the feature you are proposing?
As I presented at Hono Conf 2024, if we can mount Hono as a sub-application of Express, it would enable a gradual migration from Express to Hono, which could increase the motivation to use Hono.
As a precedent, there is a framework with a similar motivation called @fastify/express.
I already have a POC implementation in hand, but I wanted to raise an issue for discussion. How do you think?
https://github.com/sor4chi/middleware/tree/feat/express-hono-middleware/packages/express
@sor4chi Thanks!
Related to #928 by @EdamAme-x
Wow, we already had a proposal! I didn't notice because there was no issue, sorry.
@sor4chi No problem!
Let's discuss it on #928.
BTW, the code that @EdamAme-X is suggesting is Express on Hono, but what I want to propose here is Hono on Express.
In reality, it would be quite challenging for an organization that uses Express to suddenly replace it with Hono, so I would like to propose a bottom-up migration approach.
I thought so too.
Can't this package (@hono/connect and @hono/express) be combined into one?
In Hono
app.use(connect(helmet()))
In Express (Connect)
app.use(hono(secureHeaders()))
const app = express()
app.use('/hono', hono(new Hono().get('/', (c) => c.text('Hello Hono in Express!'))))
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000')
})
It seems difficult.
I am imagining the submount of this kind of Hono application.
@sor4chi @EdamAme-x
Ah, super sorry! I misunderstood. It can be enabled by using the Node Adapter:
import { Hono } from 'hono'
import { getRequestListener } from '@hono/node-server'
import express from 'express'
const hono = new Hono()
hono.get('/', (c) => c.text('Hello from Hono!'))
const app = express()
app.use('/hono', getRequestListener(hono.fetch))
app.listen(3000)
Yes, that's true, but when considering usage in an actual environment, I think it will be necessary to pass things like loggers and DB connections, which can usually be included in the context, from Express to Hono.
@yusukebe it works great.
by the way, a similar approach is offered by better-auth
the only important point is to use this before express.json() prev. body-parser
// index.ts
import { Hono } from 'hono'
import { getRequestListener } from '@hono/node-server'
import express from 'express'
const hono = new Hono()
hono.get('/', (c) => c.text('Hello from Hono!'))
const app = express()
app.use('/hono', getRequestListener(hono.fetch))
// !!! after hono routes
app.use(express.json())
app.listen(3000)
@yusukebe I found a little problem since Hono 404 is returned by default there is no way to use the root path, I wonder if there is a solution or a workaround, I would like to have some routing in the root.
// index.ts
import { Hono } from 'hono'
import { getRequestListener } from '@hono/node-server'
import express from 'express'
const app = express()
const hono = new Hono()
hono.get('/hello', (c) => c.text('Hello from Hono!'))
// 1. ⬇️
app.use('/', getRequestListener(hono.fetch))
// express middleware's
app.use(express.json())
// 2. ❌ - Now unavailable
app.get('/test', (req, res) => res.send('test'))
// sad, can't do that in Hono.
app.use((req, res, next) => {
res.status(404).send('Route not found')
})
app.listen(3000)
if there is a way to prevent the Hono error behavior or turn it off ?
what about this?
import express from 'express'
import {Hono} from 'hono';
import {getRequestListener, type HttpBindings} from '@hono/node-server';
const app = express()
const hono = new Hono<{Bindings: HttpBindings}>();
hono.get('/hono-test', (c) => c.text('Hello from Hono!'));
const honoMiddlewares = hono.router.match('*', '/*').length;
// let hono try to handle this route first
app.use((req, res, next) => {
const [match] = hono.router.match(req.method, req.path);
if (match.length > honoMiddlewares) return getRequestListener(hono.fetch)(req, res);
next(); // <--- will only be called if hono doesn't have a handler for this route
});
app.get('/foo', (req, res) => res.send('Hello from express'))
app.listen(3000)
@reslear
since Hono 404 is returned by default there is no way to use the root path, I wonder if there is a solution or a workaround, I would like to have some routing in the root.
Hmmmmm. I think there's no way because you use app.use('/', ...) and the Hono app is receiving all access.
See my solution above, should allow any route to be defined in either hono or express
Guys I had a npm package for hono sessions and have been using it in production for months now, I even shared it in hono discussions: https://github.com/orgs/honojs/discussions/3799