mirror of
https://github.com/honojs/hono.git
synced 2024-11-22 11:17:33 +01:00
docs: Add JSDoc (#1916)
* docs: Add JSDoc for App * docs: Add JSDoc for Routing * docs: Add JSDoc for Context * docs: Add JSDoc for HonoRequest * docs: Add JSDoc for Exception * add .header JSDoc & getPath JSDoc (`nabeken5:feat/add-jsdoc#1`) * add .header JSDoc & getPath JSDoc * fix: Apply the format to JSDoc --------- Thanks @EdamAme-x Co-authored-by: Kengo Watanabe <121782456+nabeken5@users.noreply.github.com> * refactor: format * feat: run denoify --------- Co-authored-by: Ame_x <121654029+EdamAme-x@users.noreply.github.com>
This commit is contained in:
parent
64b98b842d
commit
68011665fd
@ -95,10 +95,37 @@ export class Context<
|
||||
P extends string = any,
|
||||
I extends Input = {}
|
||||
> {
|
||||
/**
|
||||
* `.req` is the instance of {@link HonoRequest}.
|
||||
*/
|
||||
req: HonoRequest<P, I['out']>
|
||||
/**
|
||||
* `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers.
|
||||
* @example
|
||||
* ```ts
|
||||
* // Environment object for Cloudflare Workers
|
||||
* app.get('*', async c => {
|
||||
* const counter = c.env.COUNTER
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#env
|
||||
*/
|
||||
env: E['Bindings'] = {}
|
||||
private _var: E['Variables'] = {}
|
||||
finalized: boolean = false
|
||||
/**
|
||||
* `.error` can get the error object from the middleware if the Handler throws an error.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.use('*', async (c, next) => {
|
||||
* await next()
|
||||
* if (c.error) {
|
||||
* // do something...
|
||||
* }
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#error
|
||||
*/
|
||||
error: Error | undefined = undefined
|
||||
|
||||
#status: StatusCode = 200
|
||||
@ -122,6 +149,9 @@ export class Context<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://hono.dev/api/context#event
|
||||
*/
|
||||
get event(): FetchEventLike {
|
||||
if (this.#executionCtx && 'respondWith' in this.#executionCtx) {
|
||||
return this.#executionCtx
|
||||
@ -130,6 +160,9 @@ export class Context<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://hono.dev/api/context#executionctx
|
||||
*/
|
||||
get executionCtx(): ExecutionContext {
|
||||
if (this.#executionCtx) {
|
||||
return this.#executionCtx as ExecutionContext
|
||||
@ -138,6 +171,9 @@ export class Context<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://hono.dev/api/context#res
|
||||
*/
|
||||
get res(): Response {
|
||||
this.#isFresh = false
|
||||
return (this.#res ||= new Response('404 Not Found', { status: 404 }))
|
||||
@ -163,6 +199,40 @@ export class Context<
|
||||
this.finalized = true
|
||||
}
|
||||
|
||||
/**
|
||||
* `.render()` can create a response within a layout.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/', (c) => {
|
||||
* return c.render('Hello!')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#render-setrenderer
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
render: Renderer = (...args: any[]) => this.renderer(...args)
|
||||
|
||||
/**
|
||||
* `.setRenderer()` can set the layout in the custom middleware.
|
||||
* @example
|
||||
* ```tsx
|
||||
* app.use('*', async (c, next) => {
|
||||
* c.setRenderer((content) => {
|
||||
* return c.html(
|
||||
* <html>
|
||||
* <body>
|
||||
* <p>{content}</p>
|
||||
* </body>
|
||||
* </html>
|
||||
* )
|
||||
* })
|
||||
* await next()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#render-setrenderer
|
||||
*/
|
||||
// @ts-expect-error It is unknown how many arguments the renderer will receive.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
render: Renderer = (...args: any[]) => this.renderer(...args)
|
||||
@ -174,6 +244,20 @@ export class Context<
|
||||
this.renderer = renderer
|
||||
}
|
||||
|
||||
/**
|
||||
* `.header()` can set headers.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/welcome', (c) => {
|
||||
* // Set headers
|
||||
* c.header('X-Message', 'Hello!')
|
||||
* c.header('Content-Type', 'text/plain')
|
||||
*
|
||||
* return c.body('Thank you for coming')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#body
|
||||
*/
|
||||
header = (name: string, value: string | undefined, options?: { append?: boolean }): void => {
|
||||
// Clear the header
|
||||
if (value === undefined) {
|
||||
@ -218,15 +302,46 @@ export class Context<
|
||||
this.#status = status
|
||||
}
|
||||
|
||||
/**
|
||||
* `.set()` can set the value specified by the key.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.use('*', async (c, next) => {
|
||||
* c.set('message', 'Hono is cool!!')
|
||||
* await next()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#set-get
|
||||
```
|
||||
*/
|
||||
set: Set<E> = (key: string, value: unknown) => {
|
||||
this._var ??= {}
|
||||
this._var[key as string] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* `.get()` can use the value specified by the key.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/', (c) => {
|
||||
* const message = c.get('message')
|
||||
* return c.text(`The message is "${message}"`)
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#set-get
|
||||
*/
|
||||
get: Get<E> = (key: string) => {
|
||||
return this._var ? this._var[key] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* `.var` can access the value of a variable.
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = c.var.client.oneMethod()
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#var
|
||||
*/
|
||||
// c.var.propName is a read-only
|
||||
get var(): Readonly<E['Variables'] & ContextVariableMap> {
|
||||
return { ...this._var } as never
|
||||
@ -283,6 +398,25 @@ export class Context<
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* `.body()` can return the HTTP response.
|
||||
* You can set headers with `.header()` and set HTTP status code with `.status`.
|
||||
* This can also be set in `.text()`, `.json()` and so on.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/welcome', (c) => {
|
||||
* // Set headers
|
||||
* c.header('X-Message', 'Hello!')
|
||||
* c.header('Content-Type', 'text/plain')
|
||||
* // Set HTTP status code
|
||||
* c.status(201)
|
||||
*
|
||||
* // Return the response body
|
||||
* return c.body('Thank you for coming')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#body
|
||||
*/
|
||||
body: BodyRespond = (
|
||||
data: Data | null,
|
||||
arg?: StatusCode | ResponseInit,
|
||||
@ -293,6 +427,16 @@ export class Context<
|
||||
: this.newResponse(data, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.text()` can render text as `Content-Type:text/plain`.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/say', (c) => {
|
||||
* return c.text('Hello!')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#text
|
||||
*/
|
||||
text: TextRespond = (
|
||||
text: string,
|
||||
arg?: StatusCode | ResponseInit,
|
||||
@ -312,6 +456,16 @@ export class Context<
|
||||
: this.newResponse(text, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.json()` can render JSON as `Content-Type:application/json`.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/api', (c) => {
|
||||
* return c.json({ message: 'Hello!' })
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#json
|
||||
*/
|
||||
json: JSONRespond = <T>(
|
||||
object: InterfaceToType<T> extends JSONValue ? T : JSONValue,
|
||||
arg?: StatusCode | ResponseInit,
|
||||
@ -361,12 +515,35 @@ export class Context<
|
||||
: this.newResponse(html as string, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.redirect()` can Redirect, default status code is 302.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/redirect', (c) => {
|
||||
* return c.redirect('/')
|
||||
* })
|
||||
* app.get('/redirect-permanently', (c) => {
|
||||
* return c.redirect('/', 301)
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#redirect
|
||||
*/
|
||||
redirect = (location: string, status: StatusCode = 302): Response => {
|
||||
this.#headers ??= new Headers()
|
||||
this.#headers.set('Location', location)
|
||||
return this.newResponse(null, status)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.notFound()` can return the Not Found Response.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/notfound', (c) => {
|
||||
* return c.notFound()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#notfound
|
||||
*/
|
||||
notFound = (): Response | Promise<Response> => {
|
||||
return this.notFoundHandler(this)
|
||||
}
|
||||
|
@ -55,8 +55,38 @@ const errorHandler = (err: Error, c: Context) => {
|
||||
type GetPath<E extends Env> = (request: Request, options?: { env?: E['Bindings'] }) => string
|
||||
|
||||
export type HonoOptions<E extends Env> = {
|
||||
/**
|
||||
* `strict` option specifies whether to distinguish whether the last path is a directory or not.
|
||||
* @default true
|
||||
* @see https://hono.dev/api/hono#strict-mode
|
||||
*/
|
||||
strict?: boolean
|
||||
/**
|
||||
* `router` option specifices which router to use.
|
||||
* ```ts
|
||||
* const app = new Hono({ router: new RegExpRouter() })
|
||||
* ```
|
||||
* @see https://hono.dev/api/hono#router-option
|
||||
*/
|
||||
router?: Router<[H, RouterRoute]>
|
||||
/**
|
||||
* `getPath` can handle the host header value.
|
||||
* @example
|
||||
* ```ts
|
||||
* const app = new Hono({
|
||||
* getPath: (req) =>
|
||||
* '/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
|
||||
* })
|
||||
*
|
||||
* app.get('/www1.example.com/hello', () => c.text('hello www1'))
|
||||
*
|
||||
* // A following request will match the route:
|
||||
* // new Request('http://www1.example.com/hello', {
|
||||
* // headers: { host: 'www1.example.com' },
|
||||
* // })
|
||||
* ```
|
||||
* @see https://hono.dev/api/routing#routing-with-host-header-value
|
||||
*/
|
||||
getPath?: GetPath<E>
|
||||
}
|
||||
|
||||
@ -179,17 +209,43 @@ class Hono<
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* `.basePath()` allows base paths to be specified.
|
||||
* @example
|
||||
* ```ts
|
||||
* const api = new Hono().basePath('/api')
|
||||
* ```
|
||||
* @see https://hono.dev/api/routing#base-path
|
||||
*/
|
||||
basePath<SubPath extends string>(path: SubPath): Hono<E, S, MergePath<BasePath, SubPath>> {
|
||||
const subApp = this.clone()
|
||||
subApp._basePath = mergePath(this._basePath, path)
|
||||
return subApp
|
||||
}
|
||||
|
||||
/**
|
||||
* `.onError()` handles an error and returns a customized Response.
|
||||
* ```ts
|
||||
* app.onError((err, c) => {
|
||||
* console.error(`${err}`)
|
||||
* return c.text('Custom Error Message', 500)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
onError = (handler: ErrorHandler<E>) => {
|
||||
this.errorHandler = handler
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* `.notFound()` allows you to customize a Not Found Response.
|
||||
* ```ts
|
||||
* app.notFound((c) => {
|
||||
* return c.text('Custom 404 Message', 404)
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/hono#not-found
|
||||
*/
|
||||
notFound = (handler: NotFoundHandler<E>) => {
|
||||
this.notFoundHandler = handler
|
||||
return this
|
||||
@ -314,10 +370,26 @@ class Hono<
|
||||
})()
|
||||
}
|
||||
|
||||
/**
|
||||
* `.fetch()` will be entry point of your app.
|
||||
* @see https://hono.dev/api/hono#fetch
|
||||
*/
|
||||
fetch = (request: Request, Env?: E['Bindings'] | {}, executionCtx?: ExecutionContext) => {
|
||||
return this.dispatch(request, executionCtx, Env, request.method)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.request()` is a useful method for testing.
|
||||
* You can pass a URL or pathname to send a GET request.
|
||||
* app will return a Response object.
|
||||
* ```ts
|
||||
* test('GET /hello is ok', async () => {
|
||||
* const res = await app.request('/hello')
|
||||
* expect(res.status).toBe(200)
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/hono#request
|
||||
*/
|
||||
request = (
|
||||
input: RequestInfo | URL,
|
||||
requestInit?: RequestInit,
|
||||
@ -336,6 +408,13 @@ class Hono<
|
||||
return this.fetch(req, Env, executionCtx)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.fire()` automatically adds a global fetch event listener.
|
||||
* This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers.
|
||||
* @see https://hono.dev/api/hono#fire
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
|
||||
* @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/
|
||||
*/
|
||||
fire = () => {
|
||||
// @ts-expect-error `event` is not the type expected by addEventListener
|
||||
addEventListener('fetch', (event: FetchEventLike): void => {
|
||||
|
@ -5,6 +5,24 @@ type HTTPExceptionOptions = {
|
||||
message?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* `HTTPException` must be used when a fatal error such as authentication failure occurs.
|
||||
* @example
|
||||
* ```ts
|
||||
* import { HTTPException } from 'hono/http-exception'
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* app.post('/auth', async (c, next) => {
|
||||
* // authentication
|
||||
* if (authorized === false) {
|
||||
* throw new HTTPException(401, { message: 'Custom error message' })
|
||||
* }
|
||||
* await next()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/exception
|
||||
*/
|
||||
export class HTTPException extends Error {
|
||||
readonly res?: Response
|
||||
readonly status: StatusCode
|
||||
|
@ -25,11 +25,33 @@ type Body = {
|
||||
type BodyCache = Partial<Body & { parsedBody: BodyData }>
|
||||
|
||||
export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
/**
|
||||
* `.raw` can get the raw Request object.
|
||||
* @example
|
||||
* ```ts
|
||||
* // For Cloudflare Workers
|
||||
* app.post('/', async (c) => {
|
||||
* const metadata = c.req.raw.cf?.hostMetadata?
|
||||
* ...
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#raw
|
||||
*/
|
||||
raw: Request
|
||||
|
||||
#validatedData: { [K in keyof ValidationTargets]?: {} } // Short name of validatedData
|
||||
#matchResult: Result<[unknown, RouterRoute]>
|
||||
routeIndex: number = 0
|
||||
/**
|
||||
* `.path` can get the pathname of the request.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/about/me', (c) => {
|
||||
* const pathname = c.req.path // `/about/me`
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#path
|
||||
*/
|
||||
path: string
|
||||
bodyCache: BodyCache = {}
|
||||
|
||||
@ -44,6 +66,16 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
this.#validatedData = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* `.req.param()` gets the path parameters.
|
||||
* @example
|
||||
* ```ts
|
||||
* const name = c.req.param('name')
|
||||
* // or all parameters at once
|
||||
* const { id, comment_id } = c.req.param()
|
||||
* ```
|
||||
* @see https://hono.dev/api/routing#path-parameter
|
||||
*/
|
||||
param<P2 extends string = P>(
|
||||
key: RemoveQuestion<ParamKeys<P2>>
|
||||
): UndefinedIfHavingQuestion<ParamKeys<P2>>
|
||||
@ -76,18 +108,55 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `.query()` can get querystring parameters.
|
||||
* @example
|
||||
* ```ts
|
||||
* // Query params
|
||||
* app.get('/search', (c) => {
|
||||
* const query = c.req.query('q')
|
||||
* })
|
||||
*
|
||||
* // Get all params at once
|
||||
* app.get('/search', (c) => {
|
||||
* const { q, limit, offset } = c.req.query()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#query
|
||||
*/
|
||||
query(key: string): string | undefined
|
||||
query(): Record<string, string>
|
||||
query(key?: string) {
|
||||
return getQueryParam(this.url, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.queries()` can get multiple querystring parameter values, e.g. /search?tags=A&tags=B
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/search', (c) => {
|
||||
* // tags will be string[]
|
||||
* const tags = c.req.queries('tags')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#queries
|
||||
*/
|
||||
queries(key: string): string[] | undefined
|
||||
queries(): Record<string, string[]>
|
||||
queries(key?: string) {
|
||||
return getQueryParams(this.url, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.header()` can get the request header value.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/', (c) => {
|
||||
* const userAgent = c.req.header('User-Agent')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#header
|
||||
*/
|
||||
header(name: string): string | undefined
|
||||
header(): Record<string, string>
|
||||
header(name?: string) {
|
||||
@ -100,6 +169,16 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
return headerData
|
||||
}
|
||||
|
||||
/**
|
||||
* `.parseBody()` can parse Request body of type `multipart/form-data` or `application/x-www-form-urlencoded`
|
||||
* @example
|
||||
* ```ts
|
||||
* app.post('/entry', async (c) => {
|
||||
* const body = await c.req.parseBody()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#parsebody
|
||||
*/
|
||||
async parseBody<T extends BodyData = BodyData>(options?: ParseBodyOptions): Promise<T> {
|
||||
if (this.bodyCache.parsedBody) return this.bodyCache.parsedBody as T
|
||||
const parsedBody = await parseBody<T>(this, options)
|
||||
@ -123,14 +202,44 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
return (bodyCache[key] = raw[key]())
|
||||
}
|
||||
|
||||
/**
|
||||
* `.json()` can parse Request body of type `application/json`
|
||||
* @example
|
||||
* ```ts
|
||||
* app.post('/entry', async (c) => {
|
||||
* const body = await c.req.json()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#json
|
||||
*/
|
||||
json<T = any>(): Promise<T> {
|
||||
return this.cachedBody('json')
|
||||
}
|
||||
|
||||
/**
|
||||
* `.text()` can parse Request body of type `text/plain`
|
||||
* @example
|
||||
* ```ts
|
||||
* app.post('/entry', async (c) => {
|
||||
* const body = await c.req.text()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#text
|
||||
*/
|
||||
text(): Promise<string> {
|
||||
return this.cachedBody('text')
|
||||
}
|
||||
|
||||
/**
|
||||
* `.arrayBuffer()` parse Request body as an `ArrayBuffer`
|
||||
* @example
|
||||
* ```ts
|
||||
* app.post('/entry', async (c) => {
|
||||
* const body = await c.req.arrayBuffer()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#arraybuffer
|
||||
*/
|
||||
arrayBuffer(): Promise<ArrayBuffer> {
|
||||
return this.cachedBody('arrayBuffer')
|
||||
}
|
||||
@ -152,18 +261,70 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
return this.#validatedData[target] as unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* `.url()` can get the request url strings.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/about/me', (c) => {
|
||||
* const url = c.req.url // `http://localhost:8787/about/me`
|
||||
* ...
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#url
|
||||
*/
|
||||
get url() {
|
||||
return this.raw.url
|
||||
}
|
||||
|
||||
/**
|
||||
* `.method()` can get the method name of the request.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/about/me', (c) => {
|
||||
* const method = c.req.method // `GET`
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#method
|
||||
*/
|
||||
get method() {
|
||||
return this.raw.method
|
||||
}
|
||||
|
||||
/**
|
||||
* `.matchedRoutes()` can return a matched route in the handler
|
||||
* @example
|
||||
* ```ts
|
||||
* app.use('*', async function logger(c, next) {
|
||||
* await next()
|
||||
* c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
|
||||
* const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
|
||||
* console.log(
|
||||
* method,
|
||||
* ' ',
|
||||
* path,
|
||||
* ' '.repeat(Math.max(10 - path.length, 0)),
|
||||
* name,
|
||||
* i === c.req.routeIndex ? '<- respond from here' : ''
|
||||
* )
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#matchedroutes
|
||||
*/
|
||||
get matchedRoutes(): RouterRoute[] {
|
||||
return this.#matchResult[0].map(([[, route]]) => route)
|
||||
}
|
||||
|
||||
/**
|
||||
* `routePath()` can retrieve the path registered within the handler
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/posts/:id', (c) => {
|
||||
* return c.json({ path: c.req.routePath })
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#routepath
|
||||
*/
|
||||
get routePath(): string {
|
||||
return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path
|
||||
}
|
||||
|
177
src/context.ts
177
src/context.ts
@ -95,10 +95,37 @@ export class Context<
|
||||
P extends string = any,
|
||||
I extends Input = {}
|
||||
> {
|
||||
/**
|
||||
* `.req` is the instance of {@link HonoRequest}.
|
||||
*/
|
||||
req: HonoRequest<P, I['out']>
|
||||
/**
|
||||
* `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers.
|
||||
* @example
|
||||
* ```ts
|
||||
* // Environment object for Cloudflare Workers
|
||||
* app.get('*', async c => {
|
||||
* const counter = c.env.COUNTER
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#env
|
||||
*/
|
||||
env: E['Bindings'] = {}
|
||||
private _var: E['Variables'] = {}
|
||||
finalized: boolean = false
|
||||
/**
|
||||
* `.error` can get the error object from the middleware if the Handler throws an error.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.use('*', async (c, next) => {
|
||||
* await next()
|
||||
* if (c.error) {
|
||||
* // do something...
|
||||
* }
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#error
|
||||
*/
|
||||
error: Error | undefined = undefined
|
||||
|
||||
#status: StatusCode = 200
|
||||
@ -122,6 +149,9 @@ export class Context<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://hono.dev/api/context#event
|
||||
*/
|
||||
get event(): FetchEventLike {
|
||||
if (this.#executionCtx && 'respondWith' in this.#executionCtx) {
|
||||
return this.#executionCtx
|
||||
@ -130,6 +160,9 @@ export class Context<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://hono.dev/api/context#executionctx
|
||||
*/
|
||||
get executionCtx(): ExecutionContext {
|
||||
if (this.#executionCtx) {
|
||||
return this.#executionCtx as ExecutionContext
|
||||
@ -138,6 +171,9 @@ export class Context<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://hono.dev/api/context#res
|
||||
*/
|
||||
get res(): Response {
|
||||
this.#isFresh = false
|
||||
return (this.#res ||= new Response('404 Not Found', { status: 404 }))
|
||||
@ -163,6 +199,40 @@ export class Context<
|
||||
this.finalized = true
|
||||
}
|
||||
|
||||
/**
|
||||
* `.render()` can create a response within a layout.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/', (c) => {
|
||||
* return c.render('Hello!')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#render-setrenderer
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
render: Renderer = (...args: any[]) => this.renderer(...args)
|
||||
|
||||
/**
|
||||
* `.setRenderer()` can set the layout in the custom middleware.
|
||||
* @example
|
||||
* ```tsx
|
||||
* app.use('*', async (c, next) => {
|
||||
* c.setRenderer((content) => {
|
||||
* return c.html(
|
||||
* <html>
|
||||
* <body>
|
||||
* <p>{content}</p>
|
||||
* </body>
|
||||
* </html>
|
||||
* )
|
||||
* })
|
||||
* await next()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#render-setrenderer
|
||||
*/
|
||||
// @ts-expect-error It is unknown how many arguments the renderer will receive.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
render: Renderer = (...args: any[]) => this.renderer(...args)
|
||||
@ -174,6 +244,20 @@ export class Context<
|
||||
this.renderer = renderer
|
||||
}
|
||||
|
||||
/**
|
||||
* `.header()` can set headers.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/welcome', (c) => {
|
||||
* // Set headers
|
||||
* c.header('X-Message', 'Hello!')
|
||||
* c.header('Content-Type', 'text/plain')
|
||||
*
|
||||
* return c.body('Thank you for coming')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#body
|
||||
*/
|
||||
header = (name: string, value: string | undefined, options?: { append?: boolean }): void => {
|
||||
// Clear the header
|
||||
if (value === undefined) {
|
||||
@ -218,15 +302,46 @@ export class Context<
|
||||
this.#status = status
|
||||
}
|
||||
|
||||
/**
|
||||
* `.set()` can set the value specified by the key.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.use('*', async (c, next) => {
|
||||
* c.set('message', 'Hono is cool!!')
|
||||
* await next()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#set-get
|
||||
```
|
||||
*/
|
||||
set: Set<E> = (key: string, value: unknown) => {
|
||||
this._var ??= {}
|
||||
this._var[key as string] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* `.get()` can use the value specified by the key.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/', (c) => {
|
||||
* const message = c.get('message')
|
||||
* return c.text(`The message is "${message}"`)
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#set-get
|
||||
*/
|
||||
get: Get<E> = (key: string) => {
|
||||
return this._var ? this._var[key] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* `.var` can access the value of a variable.
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = c.var.client.oneMethod()
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#var
|
||||
*/
|
||||
// c.var.propName is a read-only
|
||||
get var(): Readonly<E['Variables'] & ContextVariableMap> {
|
||||
return { ...this._var } as never
|
||||
@ -283,6 +398,25 @@ export class Context<
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* `.body()` can return the HTTP response.
|
||||
* You can set headers with `.header()` and set HTTP status code with `.status`.
|
||||
* This can also be set in `.text()`, `.json()` and so on.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/welcome', (c) => {
|
||||
* // Set headers
|
||||
* c.header('X-Message', 'Hello!')
|
||||
* c.header('Content-Type', 'text/plain')
|
||||
* // Set HTTP status code
|
||||
* c.status(201)
|
||||
*
|
||||
* // Return the response body
|
||||
* return c.body('Thank you for coming')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#body
|
||||
*/
|
||||
body: BodyRespond = (
|
||||
data: Data | null,
|
||||
arg?: StatusCode | ResponseInit,
|
||||
@ -293,6 +427,16 @@ export class Context<
|
||||
: this.newResponse(data, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.text()` can render text as `Content-Type:text/plain`.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/say', (c) => {
|
||||
* return c.text('Hello!')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#text
|
||||
*/
|
||||
text: TextRespond = (
|
||||
text: string,
|
||||
arg?: StatusCode | ResponseInit,
|
||||
@ -312,6 +456,16 @@ export class Context<
|
||||
: this.newResponse(text, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.json()` can render JSON as `Content-Type:application/json`.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/api', (c) => {
|
||||
* return c.json({ message: 'Hello!' })
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#json
|
||||
*/
|
||||
json: JSONRespond = <T>(
|
||||
object: InterfaceToType<T> extends JSONValue ? T : JSONValue,
|
||||
arg?: StatusCode | ResponseInit,
|
||||
@ -361,12 +515,35 @@ export class Context<
|
||||
: this.newResponse(html as string, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.redirect()` can Redirect, default status code is 302.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/redirect', (c) => {
|
||||
* return c.redirect('/')
|
||||
* })
|
||||
* app.get('/redirect-permanently', (c) => {
|
||||
* return c.redirect('/', 301)
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#redirect
|
||||
*/
|
||||
redirect = (location: string, status: StatusCode = 302): Response => {
|
||||
this.#headers ??= new Headers()
|
||||
this.#headers.set('Location', location)
|
||||
return this.newResponse(null, status)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.notFound()` can return the Not Found Response.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/notfound', (c) => {
|
||||
* return c.notFound()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/context#notfound
|
||||
*/
|
||||
notFound = (): Response | Promise<Response> => {
|
||||
return this.notFoundHandler(this)
|
||||
}
|
||||
|
@ -55,8 +55,38 @@ const errorHandler = (err: Error, c: Context) => {
|
||||
type GetPath<E extends Env> = (request: Request, options?: { env?: E['Bindings'] }) => string
|
||||
|
||||
export type HonoOptions<E extends Env> = {
|
||||
/**
|
||||
* `strict` option specifies whether to distinguish whether the last path is a directory or not.
|
||||
* @default true
|
||||
* @see https://hono.dev/api/hono#strict-mode
|
||||
*/
|
||||
strict?: boolean
|
||||
/**
|
||||
* `router` option specifices which router to use.
|
||||
* ```ts
|
||||
* const app = new Hono({ router: new RegExpRouter() })
|
||||
* ```
|
||||
* @see https://hono.dev/api/hono#router-option
|
||||
*/
|
||||
router?: Router<[H, RouterRoute]>
|
||||
/**
|
||||
* `getPath` can handle the host header value.
|
||||
* @example
|
||||
* ```ts
|
||||
* const app = new Hono({
|
||||
* getPath: (req) =>
|
||||
* '/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
|
||||
* })
|
||||
*
|
||||
* app.get('/www1.example.com/hello', () => c.text('hello www1'))
|
||||
*
|
||||
* // A following request will match the route:
|
||||
* // new Request('http://www1.example.com/hello', {
|
||||
* // headers: { host: 'www1.example.com' },
|
||||
* // })
|
||||
* ```
|
||||
* @see https://hono.dev/api/routing#routing-with-host-header-value
|
||||
*/
|
||||
getPath?: GetPath<E>
|
||||
}
|
||||
|
||||
@ -179,17 +209,43 @@ class Hono<
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* `.basePath()` allows base paths to be specified.
|
||||
* @example
|
||||
* ```ts
|
||||
* const api = new Hono().basePath('/api')
|
||||
* ```
|
||||
* @see https://hono.dev/api/routing#base-path
|
||||
*/
|
||||
basePath<SubPath extends string>(path: SubPath): Hono<E, S, MergePath<BasePath, SubPath>> {
|
||||
const subApp = this.clone()
|
||||
subApp._basePath = mergePath(this._basePath, path)
|
||||
return subApp
|
||||
}
|
||||
|
||||
/**
|
||||
* `.onError()` handles an error and returns a customized Response.
|
||||
* ```ts
|
||||
* app.onError((err, c) => {
|
||||
* console.error(`${err}`)
|
||||
* return c.text('Custom Error Message', 500)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
onError = (handler: ErrorHandler<E>) => {
|
||||
this.errorHandler = handler
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* `.notFound()` allows you to customize a Not Found Response.
|
||||
* ```ts
|
||||
* app.notFound((c) => {
|
||||
* return c.text('Custom 404 Message', 404)
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/hono#not-found
|
||||
*/
|
||||
notFound = (handler: NotFoundHandler<E>) => {
|
||||
this.notFoundHandler = handler
|
||||
return this
|
||||
@ -314,10 +370,26 @@ class Hono<
|
||||
})()
|
||||
}
|
||||
|
||||
/**
|
||||
* `.fetch()` will be entry point of your app.
|
||||
* @see https://hono.dev/api/hono#fetch
|
||||
*/
|
||||
fetch = (request: Request, Env?: E['Bindings'] | {}, executionCtx?: ExecutionContext) => {
|
||||
return this.dispatch(request, executionCtx, Env, request.method)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.request()` is a useful method for testing.
|
||||
* You can pass a URL or pathname to send a GET request.
|
||||
* app will return a Response object.
|
||||
* ```ts
|
||||
* test('GET /hello is ok', async () => {
|
||||
* const res = await app.request('/hello')
|
||||
* expect(res.status).toBe(200)
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/hono#request
|
||||
*/
|
||||
request = (
|
||||
input: RequestInfo | URL,
|
||||
requestInit?: RequestInit,
|
||||
@ -336,6 +408,13 @@ class Hono<
|
||||
return this.fetch(req, Env, executionCtx)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.fire()` automatically adds a global fetch event listener.
|
||||
* This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers.
|
||||
* @see https://hono.dev/api/hono#fire
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
|
||||
* @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/
|
||||
*/
|
||||
fire = () => {
|
||||
// @ts-expect-error `event` is not the type expected by addEventListener
|
||||
addEventListener('fetch', (event: FetchEventLike): void => {
|
||||
|
@ -5,6 +5,24 @@ type HTTPExceptionOptions = {
|
||||
message?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* `HTTPException` must be used when a fatal error such as authentication failure occurs.
|
||||
* @example
|
||||
* ```ts
|
||||
* import { HTTPException } from 'hono/http-exception'
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* app.post('/auth', async (c, next) => {
|
||||
* // authentication
|
||||
* if (authorized === false) {
|
||||
* throw new HTTPException(401, { message: 'Custom error message' })
|
||||
* }
|
||||
* await next()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/exception
|
||||
*/
|
||||
export class HTTPException extends Error {
|
||||
readonly res?: Response
|
||||
readonly status: StatusCode
|
||||
|
161
src/request.ts
161
src/request.ts
@ -25,11 +25,33 @@ type Body = {
|
||||
type BodyCache = Partial<Body & { parsedBody: BodyData }>
|
||||
|
||||
export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
/**
|
||||
* `.raw` can get the raw Request object.
|
||||
* @example
|
||||
* ```ts
|
||||
* // For Cloudflare Workers
|
||||
* app.post('/', async (c) => {
|
||||
* const metadata = c.req.raw.cf?.hostMetadata?
|
||||
* ...
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#raw
|
||||
*/
|
||||
raw: Request
|
||||
|
||||
#validatedData: { [K in keyof ValidationTargets]?: {} } // Short name of validatedData
|
||||
#matchResult: Result<[unknown, RouterRoute]>
|
||||
routeIndex: number = 0
|
||||
/**
|
||||
* `.path` can get the pathname of the request.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/about/me', (c) => {
|
||||
* const pathname = c.req.path // `/about/me`
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#path
|
||||
*/
|
||||
path: string
|
||||
bodyCache: BodyCache = {}
|
||||
|
||||
@ -44,6 +66,16 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
this.#validatedData = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* `.req.param()` gets the path parameters.
|
||||
* @example
|
||||
* ```ts
|
||||
* const name = c.req.param('name')
|
||||
* // or all parameters at once
|
||||
* const { id, comment_id } = c.req.param()
|
||||
* ```
|
||||
* @see https://hono.dev/api/routing#path-parameter
|
||||
*/
|
||||
param<P2 extends string = P>(
|
||||
key: RemoveQuestion<ParamKeys<P2>>
|
||||
): UndefinedIfHavingQuestion<ParamKeys<P2>>
|
||||
@ -76,18 +108,55 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `.query()` can get querystring parameters.
|
||||
* @example
|
||||
* ```ts
|
||||
* // Query params
|
||||
* app.get('/search', (c) => {
|
||||
* const query = c.req.query('q')
|
||||
* })
|
||||
*
|
||||
* // Get all params at once
|
||||
* app.get('/search', (c) => {
|
||||
* const { q, limit, offset } = c.req.query()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#query
|
||||
*/
|
||||
query(key: string): string | undefined
|
||||
query(): Record<string, string>
|
||||
query(key?: string) {
|
||||
return getQueryParam(this.url, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.queries()` can get multiple querystring parameter values, e.g. /search?tags=A&tags=B
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/search', (c) => {
|
||||
* // tags will be string[]
|
||||
* const tags = c.req.queries('tags')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#queries
|
||||
*/
|
||||
queries(key: string): string[] | undefined
|
||||
queries(): Record<string, string[]>
|
||||
queries(key?: string) {
|
||||
return getQueryParams(this.url, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* `.header()` can get the request header value.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/', (c) => {
|
||||
* const userAgent = c.req.header('User-Agent')
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#header
|
||||
*/
|
||||
header(name: string): string | undefined
|
||||
header(): Record<string, string>
|
||||
header(name?: string) {
|
||||
@ -100,6 +169,16 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
return headerData
|
||||
}
|
||||
|
||||
/**
|
||||
* `.parseBody()` can parse Request body of type `multipart/form-data` or `application/x-www-form-urlencoded`
|
||||
* @example
|
||||
* ```ts
|
||||
* app.post('/entry', async (c) => {
|
||||
* const body = await c.req.parseBody()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#parsebody
|
||||
*/
|
||||
async parseBody<T extends BodyData = BodyData>(options?: ParseBodyOptions): Promise<T> {
|
||||
if (this.bodyCache.parsedBody) return this.bodyCache.parsedBody as T
|
||||
const parsedBody = await parseBody<T>(this, options)
|
||||
@ -123,14 +202,44 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
return (bodyCache[key] = raw[key]())
|
||||
}
|
||||
|
||||
/**
|
||||
* `.json()` can parse Request body of type `application/json`
|
||||
* @example
|
||||
* ```ts
|
||||
* app.post('/entry', async (c) => {
|
||||
* const body = await c.req.json()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#json
|
||||
*/
|
||||
json<T = any>(): Promise<T> {
|
||||
return this.cachedBody('json')
|
||||
}
|
||||
|
||||
/**
|
||||
* `.text()` can parse Request body of type `text/plain`
|
||||
* @example
|
||||
* ```ts
|
||||
* app.post('/entry', async (c) => {
|
||||
* const body = await c.req.text()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#text
|
||||
*/
|
||||
text(): Promise<string> {
|
||||
return this.cachedBody('text')
|
||||
}
|
||||
|
||||
/**
|
||||
* `.arrayBuffer()` parse Request body as an `ArrayBuffer`
|
||||
* @example
|
||||
* ```ts
|
||||
* app.post('/entry', async (c) => {
|
||||
* const body = await c.req.arrayBuffer()
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#arraybuffer
|
||||
*/
|
||||
arrayBuffer(): Promise<ArrayBuffer> {
|
||||
return this.cachedBody('arrayBuffer')
|
||||
}
|
||||
@ -152,18 +261,70 @@ export class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
||||
return this.#validatedData[target] as unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* `.url()` can get the request url strings.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/about/me', (c) => {
|
||||
* const url = c.req.url // `http://localhost:8787/about/me`
|
||||
* ...
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#url
|
||||
*/
|
||||
get url() {
|
||||
return this.raw.url
|
||||
}
|
||||
|
||||
/**
|
||||
* `.method()` can get the method name of the request.
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/about/me', (c) => {
|
||||
* const method = c.req.method // `GET`
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#method
|
||||
*/
|
||||
get method() {
|
||||
return this.raw.method
|
||||
}
|
||||
|
||||
/**
|
||||
* `.matchedRoutes()` can return a matched route in the handler
|
||||
* @example
|
||||
* ```ts
|
||||
* app.use('*', async function logger(c, next) {
|
||||
* await next()
|
||||
* c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
|
||||
* const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
|
||||
* console.log(
|
||||
* method,
|
||||
* ' ',
|
||||
* path,
|
||||
* ' '.repeat(Math.max(10 - path.length, 0)),
|
||||
* name,
|
||||
* i === c.req.routeIndex ? '<- respond from here' : ''
|
||||
* )
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#matchedroutes
|
||||
*/
|
||||
get matchedRoutes(): RouterRoute[] {
|
||||
return this.#matchResult[0].map(([[, route]]) => route)
|
||||
}
|
||||
|
||||
/**
|
||||
* `routePath()` can retrieve the path registered within the handler
|
||||
* @example
|
||||
* ```ts
|
||||
* app.get('/posts/:id', (c) => {
|
||||
* return c.json({ path: c.req.routePath })
|
||||
* })
|
||||
* ```
|
||||
* @see https://hono.dev/api/request#routepath
|
||||
*/
|
||||
get routePath(): string {
|
||||
return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user