diff --git a/docs/runtime/file-system-router.mdx b/docs/runtime/file-system-router.mdx index 0254882c6ad2..3a2a0bd05a4d 100644 --- a/docs/runtime/file-system-router.mdx +++ b/docs/runtime/file-system-router.mdx @@ -59,6 +59,18 @@ router.match("/settings?foo=bar"); } ``` +A name that appears more than once in the query string maps to an array of its values. + +```ts +router.match("/settings?foo=bar&foo=baz&page=2").query; + +// => +{ + foo: ["bar", "baz"], + page: "2" +} +``` + The router parses URL parameters and returns them in the `params` property: ```ts @@ -77,6 +89,18 @@ router.match("/blog/my-cool-post"); } ``` +`query` also contains the route parameters. A route parameter wins over a query string entry with the same name. + +```ts +router.match("/blog/my-cool-post?slug=other&page=2").query; + +// => +{ + slug: "my-cool-post", + page: "2" +} +``` + The `.match()` method also accepts `Request` and `Response` objects; the router uses their `url` property to resolve the route. ```ts @@ -111,7 +135,7 @@ interface Bun { pathname: string; src: string; params?: Record; - query?: Record; + query?: Record; } | null } } diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index d8c9402e2871..d29c14cf4edc 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -8390,7 +8390,11 @@ declare module "bun" { match(input: string | Request | Response): MatchedRoute | null; readonly assetPrefix: string; - readonly origin: string; + /** + * The `origin` passed to the constructor. `null` when the router was + * created without one (or with `""`). + */ + readonly origin: string | null; readonly style: string; readonly routes: Record; @@ -8417,7 +8421,24 @@ declare module "bun" { readonly params: Record; readonly filePath: string; readonly pathname: string; - readonly query: Record; + /** + * The parsed query string, merged with {@link MatchedRoute.params}. A + * route parameter wins over a query string entry of the same name. A + * name that appears more than once in the query string maps to an array + * of its values. + * + * @example + * ```ts + * // with a pages/blog/[slug].tsx route: + * const router = new FileSystemRouter({ + * dir: "/path/to/pages", + * style: "nextjs", + * }); + * router.match("/blog/hello?tag=a&tag=b&page=2")?.query; + * // { slug: "hello", tag: ["a", "b"], page: "2" } + * ``` + */ + readonly query: Record; readonly name: string; readonly kind: "exact" | "catch-all" | "optional-catch-all" | "dynamic"; readonly src: string; diff --git a/test/integration/bun-types/fixture/fsrouter.ts b/test/integration/bun-types/fixture/fsrouter.ts index d3a13d039c74..afd0ff00e1c2 100644 --- a/test/integration/bun-types/fixture/fsrouter.ts +++ b/test/integration/bun-types/fixture/fsrouter.ts @@ -6,8 +6,16 @@ const router = new FileSystemRouter({ style: "nextjs", }); +// null when the router was constructed without an origin. +expectType(router.origin).is(); + const match = router.match("/"); expectType(match?.name!); expectType(match?.pathname!); -expectType>(match?.query!); +// A query string name given more than once maps to an array of its values. +expectType(match?.query!).is>(); +for (const value of Object.values(match!.query)) { + if (Array.isArray(value)) expectType(value).is(); + else expectType(value).is(); +} expectType>(match?.params!);