diff --git a/.changeset/light-pianos-sip.md b/.changeset/light-pianos-sip.md new file mode 100644 index 000000000000..c9f979c37cb3 --- /dev/null +++ b/.changeset/light-pianos-sip.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Handles route collision detection only if it matches `getStaticPaths` diff --git a/packages/astro/src/core/render/params-and-props.ts b/packages/astro/src/core/render/params-and-props.ts index cf7d02d48360..6f5ec19f6339 100644 --- a/packages/astro/src/core/render/params-and-props.ts +++ b/packages/astro/src/core/render/params-and-props.ts @@ -33,12 +33,6 @@ export async function getProps(opts: GetParamsAndPropsOptions): Promise { return {}; } - // This is a dynamic route, start getting the params - const params = getParams(route, pathname); - if (mod) { - validatePrerenderEndpointCollision(route, mod, params); - } - // During build, the route cache should already be populated. // During development, the route cache is filled on-demand and may be empty. const staticPaths = await callGetStaticPaths({ @@ -49,6 +43,7 @@ export async function getProps(opts: GetParamsAndPropsOptions): Promise { ssr: serverLike, }); + const params = getParams(route, pathname); const matchedStaticPath = findPathItemByKey(staticPaths, params, route, logger); if (!matchedStaticPath && (serverLike ? route.prerender : true)) { throw new AstroError({ @@ -58,6 +53,10 @@ export async function getProps(opts: GetParamsAndPropsOptions): Promise { }); } + if (mod) { + validatePrerenderEndpointCollision(route, mod, params); + } + const props: Props = matchedStaticPath?.props ? { ...matchedStaticPath.props } : {}; return props; diff --git a/packages/astro/test/dynamic-endpoint-collision.test.js b/packages/astro/test/dynamic-endpoint-collision.test.js index b1aa42f9fdb8..91bbf3b67e87 100644 --- a/packages/astro/test/dynamic-endpoint-collision.test.js +++ b/packages/astro/test/dynamic-endpoint-collision.test.js @@ -49,5 +49,10 @@ describe('Dynamic endpoint collision', () => { const res = await fixture.fetch('/api/catch/one').then((r) => r.text()); assert.equal(res, '{"slug":"one"}'); }); + + it('returns 404 when user visits dynamic endpoint that has collision but not specified in getStaticPaths', async () => { + const res = await fixture.fetch('/api/safe'); + assert.equal(res.status, 404); + }); }); }); diff --git a/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/safe/[...slug].ts b/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/safe/[...slug].ts new file mode 100644 index 000000000000..98fecec1cc1e --- /dev/null +++ b/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/safe/[...slug].ts @@ -0,0 +1,14 @@ +import type { APIRoute } from 'astro'; + +// No undefined so should not error +const slugs = ['one']; + +export const GET: APIRoute = ({ params }) => { + return Response.json({ + slug: params.slug || 'index', + }); +}; + +export function getStaticPaths() { + return slugs.map((u) => ({ params: { slug: u } })); +}