-
-
Notifications
You must be signed in to change notification settings - Fork 642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HMR for server routes #3817
Comments
Draft for implementation of /src/router/smart-router/router.ts: export class SmartRouter<T> implements Router<T> {
name: string = 'SmartRouter'
#routers: Router<T>[] = []
- #routes?: [string, string, T][] = []
+ #routes?: Record<string, T> = {}
constructor(init: { routers: Router<T>[] }) {
this.#routers = init.routers
}
add(method: string, path: string, handler: T) {
if (!this.#routes) {
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT)
}
- this.#routes.push([method, path, handler])
+ this.#routes[`${path}${method}`] = handler
}
match(method: string, path: string): Result<T> {
if (!this.#routes) {
throw new Error('Fatal error')
}
const routers = this.#routers
const routes = this.#routes
const len = routers.length
let i = 0
let res
for (; i < len; i++) {
const router = routers[i]
try {
- for (let i = 0, len = routes.length; i < len; i++) {
- router.add(...routes[i])
+ for (const [key, handler] of Object.entries(routes)) {
+ const [path, method] = key.split('')
+ router.add(method, path, handler)
}
res = router.match(method, path)
} catch (e) {
if (e instanceof UnsupportedPathError) {
continue
}
throw e
}
this.match = router.match.bind(router)
this.#routers = [router]
- this.#routes = undefined
break
} |
Have you ever tried |
@yusukebe Yes, I did some try but got some trouble (vite config conflicts) with Vike ecosystem. How can an updated handler be injected in Hono context to replace the handler of an already regirtered route? |
I created a simple register object (hmrRoutes) that will be dispatched on related routes. // /server.ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const filepath = './api/index.ts'
const app = new Hono()
// Register the file to intercept changes
const { default: handler } = await viteDevServer.ssrLoadModule(filepath, { fixStacktrace: true })
// Register route first time
app.get('/', handler)
// Inject section
app.hmrRoutes = {}
app.injectHandler = function (method, route, handler) {
app.hmrRoutes[`${method}${route}`] = handler
}
// Hook into the dispatch mechanism to process dynamic routes
const originalDispatch = app.dispatch
app.dispatch = async function (context) {
const { method, routePath } = context.req
const handler = app.hmrRoutes[`${method}${routePath}`]
if (handler) {
// Dispatch with re-registered hander
return await handler(context)
}
// Default dispatch
return originalDispatch.call(this, context)
}
// Event for file changes
viteDevServer.watcher.on('change', async (file) => {
const { default: handler } = await viteDevServer.ssrLoadModule(filepath, { fixStacktrace: true })
// <Code to get method and route values>
// Re-register route with the new file handler
app.injectHandler(method, route, handler)
})
serve({ fetch: app.fetch }) @yusukebe why the dispatch function isn't called after a request? |
That is the limitation of the routers. You can't do it. |
@yusukebe I fixed using a middleware and now it works! // /server.ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const filepath = './api/index.ts'
const app = new Hono()
// Middleware to dispatch with re-registered handler
// Call before register routes
app.use(`*`, async (c, next) => {
const handler = app.hmrRoutes[`${c.env.incoming.method}${c.env.incoming.url}`]
if (handler) {
return await handler(c)
}
await next()
})
// Register the file to intercept changes
const { default: handler } = await viteDevServer.ssrLoadModule(filepath, { fixStacktrace: true })
// Register route
app.get('/', handler)
app.hmrRoutes = {}
app.injectHandler = function (method, route, handler) {
// Inject route with new handler
app.hmrRoutes[`${method.toUpperCase()}${route}`] = handler
}
// Event for file changes
viteDevServer.watcher.on('change', async (file) => {
const { default: handler } = await viteDevServer.ssrLoadModule(filepath, { fixStacktrace: true })
// <Code to get method and route values>
// Re-register route with the new file handler
app.injectHandler(method, route, handler)
})
serve({ fetch: app.fetch }) |
Having the code, could you create a new official package for Hono inside @hono/vite-plugins? |
What is the feature you are proposing?
During development environment, HMR (Hot Module Replacement) is needed to develop: after the server load all route handlers, any change to to handler must be updated to the server.
HMR improves the DX (Developer Experience) without manually restart the server every time a file is changed.
Example with vite
Having a file handler:
and the server file:
Actual
On re-register an handler, Hono routers throws an Error:
Expected
On a re-register route, Hono routers should replace the old handler with the new one.
Implementation (draft)
Some points that can be achieved one or all to the check of Hono routers:
add
process.env.NODE_ENV === 'production'
to condition of the check. Eg:the error is provided only for new routes (check current method and route with the map of routes). Eg:
Initial discussion: #3805
The text was updated successfully, but these errors were encountered: