Skip to content

Commit

Permalink
qol(routing): warn when api route method doesn't match the casing of …
Browse files Browse the repository at this point in the history
…an export (#9497)

* fix(routing): improve messaging for getting the case wrong

* add changeset

* lint: no shadowing

* remove old APIRoute signature

* Apply suggestions from code review

Co-authored-by: Voxel <[email protected]>

---------

Co-authored-by: Voxel <[email protected]>
  • Loading branch information
lilnasy and VoxelMC authored Dec 27, 2023
1 parent a171c22 commit 7f7a7f1
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/tidy-dogs-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Adds a helpful warning message for when an exported API Route is not uppercase.
2 changes: 1 addition & 1 deletion packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2237,7 +2237,7 @@ export type APIRoute<Props extends Record<string, any> = Record<string, any>> =
) => Response | Promise<Response>;

export interface EndpointHandler {
[method: string]: APIRoute | ((params: Params, request: Request) => Response);
[method: string]: APIRoute;
}

export type Props = Record<string, unknown>;
Expand Down
46 changes: 15 additions & 31 deletions packages/astro/src/runtime/server/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,6 @@ import { bold } from 'kleur/colors';
import type { APIContext, EndpointHandler, Params } from '../../@types/astro.js';
import type { Logger } from '../../core/logger/core.js';

function getHandlerFromModule(mod: EndpointHandler, method: string) {
// If there was an exact match on `method`, return that function.
if (mod[method]) {
return mod[method];
}
if (mod['ALL']) {
return mod['ALL'];
}
// Otherwise, no handler found.
return undefined;
}

/** Renders an endpoint request to completion, returning the body. */
export async function renderEndpoint(
mod: EndpointHandler,
Expand All @@ -23,38 +11,34 @@ export async function renderEndpoint(
) {
const { request, url } = context;

const chosenMethod = request.method?.toUpperCase();
const handler = getHandlerFromModule(mod, chosenMethod);
if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'GET') {
const method = request.method.toUpperCase();
// use the exact match on `method`, fallback to ALL
const handler = mod[method] ?? mod['ALL'];
if (!ssr && ssr === false && method !== 'GET') {
logger.warn(
null,
"router",
`${url.pathname} ${bold(
chosenMethod
method
)} requests are not available for a static site. Update your config to \`output: 'server'\` or \`output: 'hybrid'\` to enable.`
);
}
if (!handler || typeof handler !== 'function') {
if (typeof handler !== 'function') {
logger.warn(
'router',
`No API Route handler exists for the method "${method}" for the route ${url.pathname}.\n` +
`Found handlers: ${Object.keys(mod).map(exp => JSON.stringify(exp)).join(', ')}\n` +
('all' in mod ? `One of the exported handlers is "all" (lowercase), did you mean to export 'ALL'?\n` : '')
);
// No handler found, so this should be a 404. Using a custom header
// to signal to the renderer that this is an internal 404 that should
// be handled by a custom 404 route if possible.
let response = new Response(null, {
return new Response(null, {
status: 404,
headers: {
'X-Astro-Response': 'Not-Found',
},
});
return response;
}

const proxy = new Proxy(context, {
get(target, prop) {
if (prop in target) {
return Reflect.get(target, prop);
} else {
return undefined;
}
},
}) as APIContext & Params;

return handler.call(mod, proxy, request);
return handler.call(mod, context);
}

0 comments on commit 7f7a7f1

Please sign in to comment.