honojs / hono

Web framework built on Web Standards
https://hono.dev
MIT License
18.64k stars 523 forks source link

Hono middleware matches all routes instead of the routes declared after the middleware #3361

Open abdurahmanshiine opened 2 weeks ago

abdurahmanshiine commented 2 weeks ago

What version of Hono are you using?

4.5.10

What runtime/platform is your app running on?

Bun

What steps can reproduce the bug?

This is my code:

import { Hono } from 'hono'
import { logger } from "hono/logger"

const app = new Hono()
const auth = new Hono().basePath("/auth")
const books = new Hono().basePath("/books")
const apiRoutes = new Hono()

app.use("*", logger())

auth.get('/before', (c) => {
  return c.text('Before middleware!')
})

app.use(async (c, next) => {
  console.log("fired")
  await next()
})

auth.get('/after', (c) => {
  return c.text('After middleware!')
})

books.get('/book', (c) => {
  return c.text('Received book!')
})

apiRoutes.route("/", auth)
apiRoutes.route("/", books)

app.route("/", apiRoutes)

export default {
  port: 5555,
  fetch: app.fetch,
}

What is the expected behavior?

The expected behavior is that the middleware isn't run for the first request /auth/before since it's declared after the route handler, while it should fire for all the other requests that are declared after it

What do you see instead?

The middleware is fired for all the routes regardless

Additional information

No response

MathurAditya724 commented 2 weeks ago

It looks like the issue arises because the middleware is being defined at the global scope within your app. When you define middleware this way, it will be applied to all routes in the app, which is why it’s being fired for every request, including /auth/before.

If you want the middleware to run only for specific routes, you'll need to define it specifically for those routes or within those route groups. You can encapsulate the middleware in a separate function and then apply it only where needed. Here's an example:

import { Hono } from 'hono'
import { logger } from "hono/logger"

const app = new Hono()
const auth = new Hono().basePath("/auth")
const books = new Hono().basePath("/books")
const apiRoutes = new Hono()

// Also you can write it like this, this is better
app.use(logger())

// Encapsulate middleware in a function
const myMiddleware = async (c, next) => {
  console.log("fired")
  await next()
}

// Apply middleware only to certain routes
auth.use('/after', myMiddleware)

auth.get('/before', (c) => {
  return c.text('Before middleware!')
})

auth.get('/after', (c) => {
  return c.text('After middleware!')
})

books.get('/book', (c) => {
  return c.text('Received book!')
})

apiRoutes.route("/", auth)
apiRoutes.route("/", myMiddleware, books)

app.route("/", apiRoutes)

export default {
  port: 5555,
  fetch: app.fetch,
}

In this setup, the middleware myMiddleware will only run for the /auth/after and /book routes, as intended.