GitShow/honojs/hono
honojs

hono

Web framework built on Web Standards

by honojs
aws-lambdabuncloudflarecloudflare-workersdenonpmroutertypescript
Star on GitHubForkWebsitenpm

TypeScript

31.7k stars1.2k forks351 contributorsActive · 5h agoSince 2021v4.13.2MIT

Meet the team

See all 351 on GitHub →
yusukebe
yusukebe1.7k contributions
usualoma
usualoma234 contributions
EdamAme-x
EdamAme-x71 contributions
watany-dev
watany-dev53 contributions
ryuapp
ryuapp31 contributions
nakasyou
nakasyou27 contributions
metrue
metrue24 contributions
exoego
exoego21 contributions

Languages

View on GitHub →
TypeScript99.7%
JavaScript0.2%
Shell0%
HTML0%

Commit activity

Last 12 weeks · 131 commits

Full graph →

Community health

5 of 6 standards met

Community profile →
75
✓README✓License✓Contributing✓Code of Conduct○Issue Template✓PR Template

Recent PRs & issues

Active · Last activity 5h ago
See all on GitHub →
usualoma
[PoC] feat: dispatch notFound and onError handlers as internal routesOpenPR

Taken in isolation as a solution to #5193, this may look over-engineered. The broader goal of this PR is to redefine the semantics of notFound() and onError() using Hono’s own routing model. Despite that redefinition, most existing applications should not require any changes. The implementation does increase the bundle size, but the minified increase has been kept below 1%. In particular, the single-handler fast path used by requests without middleware remains structurally unchanged, and local benchmarks show no performance regression. Summary This draft explores treating and definitions as internal routes. This enables middleware to be registered specifically for not-found and error handling: Instead of composing these handlers separately from routing, Hono registers them with reserved internal methods ( and ) and dispatches matching internal routes through the configured Router. [!IMPORTANT] Although existing single-handler calls remain source-compatible, this changes fallback registration and dispatch semantics and should likely be considered for a major version. Why internal routes? A definition such as: can be understood as one route definition that is internally dispatched when normal routing produces no response. This gives fallback handlers the useful properties of normal route handlers: middleware composition base-path and mount-path scoping sub-application flattening Router-level wrapping and instrumentation preservation of metadata describing where an error or explicit not-found originated The internal dispatch reuses the existing , request path, and route metadata. It does not create another or restart the whole application middleware chain. Semantics changed by this PR and accept middleware Both methods now accept zero or more middleware handlers followed by the existing final handler. The final handler signatures are unchanged: Fallback middleware follows normal route middleware semantics: it may call it may modify the response after it may return a response without calling to stop the chain Fallback definitions are flattened routes Each fallback definition is registered at the current base path using an internal method. When a sub-application is mounted, its internal routes are flattened into the parent Router together with its normal routes: The mounted not-found definition behaves like an internal route registered at . During fallback dispatch, Hono matches internal routes against the original request path and composes the matching handlers in normal registration order. In the example above: uses the API not-found handler uses the app not-found handler This applies equally to: an implicit 404 a returned an error thrown by a handler or middleware Fallback selection does not depend on which application originally registered the normal handler. After flattens a sub-application, its internal routes participate in the mounted routing space. For example, a parent route registered directly at may use the mounted API fallback because the request path matches . Likewise, an error thrown by parent middleware while handling may be handled by the mounted API error route. Registration order determines fallback ordering Matching internal routes are composed in the same registration order as normal routes. No implicit “most specific scope wins” rule is added. If a root fallback is registered before a scoped fallback, the root final handler may produce a response before the scoped fallback is reached: Conversely, registering or mounting the scoped fallback first allows it to handle its matching path before the root fallback: Since every registration ends with a final handler that does not receive , the first matching registration normally finalizes the response and stops the remaining chain. This also changes repeated registrations from the previous “last assignment wins” behavior to normal route-chain ordering. Error origin metadata is preserved Fallback selection uses the request path and registration order, but internal dispatch does not overwrite the original route index. Route parameters and origin-sensitive information such as therefore continue to describe the original route while fallback middleware runs. If middleware throws after , Hono restores the route index to the middleware that actually threw before dispatching the error route. This separates two concerns: the request path and Router determine which fallback routes run the original route metadata describes where the fallback originated Internal routes are visible in The raw array includes entries whose methods begin with . The prefix is reserved for internal routes. Internal methods are excluded from: / the header generated by Code that directly consumes may need to make the same distinction. If exposing internal routes through is undesirable, they can instead be kept in a completely separate internal collection. That is possible, but adds roughly another 130 B to the minified bundle. Semantics preserved The final and handler signatures are unchanged. Existing single-handler registrations remain valid. is not a control-flow terminator. Its response must still be returned or awaited when it should become the route response. The default 404 and 500 responses are unchanged. Default handling is unchanged. Internal dispatch reuses the original . Route parameters remain available while fallback middleware runs. Global middleware is not executed a second time during internal dispatch. An error thrown by an error handler is still propagated. Successful normal routes do not pass through not-found or error middleware. For example, this retains its existing behavior: To use the not-found response, the handler must return it: Router integration Internal handlers are registered using the configured Router’s existing method and selected through . This broadens the scope of router-decoration features such as the one proposed in #5201. By including not-found and error handlers in routing, a Router wrapper can wrap, instrument, or otherwise decorate these handlers alongside normal route handlers. Performance Internal routing is only entered when: normal routing does not produce a response a returned is dispatched a handler throws an error It does not add another Router match to successful requests. The existing successful-request execution structure is retained: The single-handler fast path remains in place, and no global wrapper or fallback middleware layer is added to every request. For successful requests involving middleware, now evaluates one additional boolean condition when updating the route index. A very small regression is therefore theoretically possible. A local 15-round microbenchmark measured: These differences are within the observed benchmark noise. For mounted sub-applications with a custom , the previous wrapper around every imported route is removed. Bundle size Measured from clean builds based on using the existing bundle-size configuration. Compressed sizes were generated deterministically with gzip level 9 and Brotli quality 11. The internal methods are defined inside ; they are not added to the public Router API. Appendix: Design alternatives and trade-offs Several fallback-selection and storage models were explored. Where multiple designs were semantically viable, this branch generally chose the smaller implementation in order to keep the minified bundle-size increase below 1%. The alternatives below are therefore not necessarily rejected designs: if a larger bundle-size increase is acceptable, some of them can be reintroduced. Each section also describes any semantic or performance trade-offs that would still need to be considered. Selecting fallbacks from the originating application An earlier implementation preserved the base path of the normal route that initiated fallback dispatch. It selected: 1. a fallback registered at the exact originating base path 2. the root fallback when no exact definition existed This prevented a parent route whose URL happened to be below a mounted application from entering the mounted application’s fallback. It also meant that: an implicit used the root fallback because it had no originating route a parent handler registered directly at used the parent fallback a handler imported through used the sub-application fallback This provides a stronger application boundary, but otherwise flattens paths and middleware into the parent routing space. Using handler provenance only for fallback selection therefore introduced a separate ownership model that did not apply to normal routing. The implementation also required carrying the originating base path through dispatch, selecting an exact scope manually, and forwarding that scope through error handling. The prototype added approximately another 200 B to the minified bundle compared with the selected flattening model. The current design keeps origin metadata for inspection but does not use it to select a fallback. Selecting the most specific matching scope Another prototype selected only the deepest matching internal route, independently of registration order. This makes a scoped fallback reliably override a root fallback: Under that model, would always use , regardless of which definition was registered first. However, Hono normally composes matching routes in registration order. Adding an independent specificity rule only for fallback routes would introduce a second ordering model. It also required scanning matched routes, comparing scopes, and extracting only the selected route group. A segment-aware implementation added approximately 126 B to the minified bundle compared with composing matching internal routes directly. Using raw string length was smaller, but was rejected because dynamic parameter names and path syntax do not provide a reliable measure of route specificity. The current implementation therefore follows normal Hono registration order. Keeping internal routes outside Internal routes could be stored in a completely separate collection while still being registered with the configured Router. This would prevent raw consumers from seeing methods such as and . However, it would require separate storage and mounting logic for internal routes. The prototype increased the minified bundle by roughly another 130 B. The selected implementation keeps a single route collection and filters reserved methods from user-facing helpers and the header. Making a control-flow terminator A terminal model was also considered, where calling would immediately stop the current handler even if its result was not returned: Implementing this required a special control-flow signal to survive middleware composition and asynchronous handlers. It also changed how user / blocks and upstream middleware observed the call. The additional machinery was relatively large, and the behavior would differ substantially from the existing function-call semantics. The current implementation therefore preserves the requirement to return or await . Reusing the normal request dispatch path Internal fallback could have been implemented by re-entering the complete normal dispatch path with a reserved method or internal URL. That would reduce the conceptual distinction between normal and fallback routing, but would either: create another or execute global middleware a second time add conditionals or wrappers to the successful-request path These consequences conflict with the goal of preserving the original context and normal-request performance. The selected implementation performs a separate Router match only after fallback dispatch has already been requested. Retaining per-route error wrappers for mounted applications Previously, mounting a sub-application with a custom wrapped every imported handler so that errors could be delegated to the sub-application’s stored error handler. That preserved the behavior despite living outside the route collection, but added a wrapper to normal execution and prevented Router-level decoration from treating the error handler as a route. Registering as an internal route allows it to be flattened directly and removes the wrapper from successful requests. The author should do the following, if applicable [x] Add tests [x] Run tests [x] to format the code [x] Add TSDoc/JSDoc where applicable

usualoma · 4m ago
max-got
Feature Request: Bearer Auth - allow custom error response content typeOpenIssue

What is the feature you are proposing? Problem Bearer auth error responses can't be sent as anything other than or . The content type is hardcoded in ... Request Since we want to adhere to RFC 9457 problem details () everywhere, it would be nice if we are able to customize the content type in the response. Since the middleware throws an with a pre-built , an handler can't fix the headers afterwards either. See for Reference bearer-auth/index.ts Idea Let also return a and use it as-is (set if missing). Backwards compatible. Alternatively a option next to (?) Current workaround: throw from the message function and rebuild the header manually in the app.

max-got · 15m ago
contactjawad
fix(etag): match If-None-Match tags with optional whitespace before the commaOpenPR

Fixes the ETag middleware so matches when optional whitespace precedes the comma in the tag list. What The ETag middleware returned with the full body instead of when a conditional request's header placed whitespace before a delimiting comma (e.g. ), even though the current ETag was present in the list. Why split the header with , which only consumes whitespace after each comma. RFC 9110 defines as a list, and the list rule permits optional whitespace (OWS) on both sides of the delimiter. When OWS preceded a comma, the preceding tag kept a trailing space, so and the match silently failed. Any tag not in the last list position was affected, causing an unnecessary full-body transfer instead of a cheap . The fix trims the header and splits on , so OWS on either side of the comma is consumed. A regression test covers a matching tag with a space before the following comma. The author should do the following, if applicable [x] Add tests [x] Run tests [x] to format the code [ ] Add TSDoc/JSDoc to document the code

contactjawad · 3h ago

Recent fixes

View closed PRs →
ErfanBagheri404
fix(router): match suffix wildcard routes when sub-app has static+param siblingsMergedPR

Fixes #5219 treated as a single literal segment , so TrieRouter never created the wildcard child node. The route returned 404 whenever a mounted sub-app registered sibling routes. Now a trailing is split into its own segment, matching the behavior of . Added regression tests covering , , and sibling matching.

ErfanBagheri404 · 56m ago
santhiprakash
fix(method-not-allowed): ignore trailing wildcard routes when inferring AllowMergedPR

Problem Fixes #5223 When collects allowed methods from registered routes, it treats trailing-wildcard routes (e.g. or ) as evidence that every path exists. This converts otherwise-unhandled requests in that namespace from 404 to 405. For example, with: A request incorrectly returns 405 instead of 404, because the wildcard contributes to the Allow map for every path. Root cause The middleware builds a map from all registered routes without filtering out trailing-wildcard paths. A trailing wildcard matches an arbitrary path suffix — it does not prove that a particular target resource exists. Fix Skip routes whose path ends with when building the Allow map. Concrete routes (e.g. ) still produce 405 for other methods. GET/HEAD still reach the wildcard handler itself. This follows the direction discussed in #5223: "routes whose paths end in should be ignored when inferring allowed methods". Verification — 23 passed (3 new regression tests) New tests cover: Slash-star wildcards () alongside concrete routes Suffix wildcards () alongside concrete routes Mixed concrete + wildcard routes in a single app and clean

santhiprakash · 2h ago
Structured data for AI agents

Repository: honojs/hono. Description: Web framework built on Web Standards Stars: 31668, Forks: 1221. Primary language: TypeScript. Languages: TypeScript (99.7%), JavaScript (0.2%), Shell (0%), HTML (0%). License: MIT. Homepage: https://hono.dev Topics: aws-lambda, bun, cloudflare, cloudflare-workers, deno, npm, router, typescript, web-framework. Latest release: v4.13.2 (1d ago). Open PRs: 100, open issues: 270. Last activity: 5h ago. Community health: 75%. Top contributors: yusukebe, usualoma, EdamAme-x, watany-dev, ryuapp, nakasyou, metrue, exoego, sor4chi, yasuaki640 and others.

·@ofershap

Replace github.com with gitshow.dev