Skip to content

feat(middleware): add Method Not Allowed middleware#4751

Open
rokasta12 wants to merge 2 commits intohonojs:mainfrom
rokasta12:feat/method-not-allowed
Open

feat(middleware): add Method Not Allowed middleware#4751
rokasta12 wants to merge 2 commits intohonojs:mainfrom
rokasta12:feat/method-not-allowed

Conversation

@rokasta12
Copy link
Copy Markdown
Contributor

Summary

Add a new methodNotAllowed middleware that returns 405 Method Not Allowed with an Allow header when a request matches a registered route path but the HTTP method is not supported.

This implements the approach proposed by @usualoma in #4633 (comment) and confirmed by @yusukebe (comment).

Closes #4633

Usage

import { Hono } from 'hono'
import { methodNotAllowed } from 'hono/method-not-allowed'

const app = new Hono()

app.use(methodNotAllowed({ app }))

app.get('/hello', (c) => c.text('Hello!'))
app.post('/hello', (c) => c.text('Posted!'))

// GET /hello  => 200 "Hello!"
// POST /hello => 200 "Posted!"
// PUT /hello  => 405 Method Not Allowed (Allow: GET, POST)
// GET /foo    => 404 Not Found

Implementation Details

  • Takes the app instance as an argument (same pattern as methodOverride)
  • Lazily builds an internal TrieRouter from app.routes on first 404 encounter
  • Checks if the request path matches any registered route with a different method
  • Throws HTTPException(405) with Allow header listing valid methods (per RFC 7231)
  • Skips routes registered with app.all() / app.use() (METHOD_NAME_ALL) when collecting methods
  • Deduplicates methods in the Allow header

Tests (16 passing)

Test Description
Valid GET/POST requests Returns 200 as normal
Unsupported method on existing path Returns 405 with "Method Not Allowed" body
Allow header Lists permitted methods, excludes request method
Non-existent path Returns 404 (not 405)
Parameterized routes (:id) Correctly returns 405 with all allowed methods
Works alongside other middleware app.use() middleware doesn't break 405 detection
Sub-applications app.route('/sub', sub) routes are handled correctly
HEAD requests on GET routes Returns 200 (Hono handles HEAD automatically)
OPTIONS when explicitly defined Returns the defined response, not 405
app.all() routes No 405 — any method is valid

Checklist

  • Tests pass: 16/16
  • 100% code coverage (statements, branches, functions, lines)
  • TypeScript: npx tsc --noEmit — zero errors
  • Formatting: bun run format:fix — no changes needed
  • Linting: npx eslint src/middleware/method-not-allowed/ — zero warnings
  • Export entries added to package.json (exports + typesVersions) and jsr.json
  • Export validation: build/validate-exports.test.ts passes

rokasta12 and others added 2 commits February 22, 2026 16:18
Add a new `methodNotAllowed` middleware that returns 405 with an Allow
header when a request path matches a registered route but the HTTP method
is not supported. This follows the implementation approach proposed by
@usualoma in honojs#4633 and confirmed by @yusukebe.

The middleware:
- Takes the app instance as an argument (same pattern as methodOverride)
- Lazily builds an internal TrieRouter from app.routes on first 404
- Checks if the path matches any registered route with a different method
- Throws HTTPException(405) with Allow header listing valid methods
- Does not interfere with app.all() routes or normal 404s

Closes honojs#4633
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.51%. Comparing base (2de30d7) to head (11fe723).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4751      +/-   ##
==========================================
+ Coverage   91.48%   91.51%   +0.03%     
==========================================
  Files         177      178       +1     
  Lines       11556    11599      +43     
  Branches     3357     3370      +13     
==========================================
+ Hits        10572    10615      +43     
  Misses        983      983              
  Partials        1        1              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@yashmakhija
Copy link
Copy Markdown

Clean implementation. One edge case: the methodNotAllowedRouter is built and cached on first 404. If routes are added dynamically after that, they won't be reflected in the cached router.

Probably fine for typical use (routes defined at startup), but worth a note in the docs that this middleware assumes routes are registered before the first request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return 405 Method Not Allowed when a path exists but the HTTP method is unsupported

2 participants