summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Arsh <69170106+lilnasy@users.noreply.github.com> 2024-03-18 20:06:46 +0530
committerGravatar GitHub <noreply@github.com> 2024-03-18 20:06:46 +0530
commitc12666166db724915e42e37a048483c99f88e6d9 (patch)
tree35675c0f5a3def888865252e3b733db14ccb6e33
parent7138aa46781bc2ee3fb17a2b2d2d01cf94560726 (diff)
downloadastro-c12666166db724915e42e37a048483c99f88e6d9.tar.gz
astro-c12666166db724915e42e37a048483c99f88e6d9.tar.zst
astro-c12666166db724915e42e37a048483c99f88e6d9.zip
qol(endpoints): helpful error message when a response is not provided (#10455)
* qol(endpoints): helpful error message when a response is not provded * add changeset * add test
-rw-r--r--.changeset/cuddly-suits-hunt.md5
-rw-r--r--packages/astro/src/core/errors/errors-data.ts23
-rw-r--r--packages/astro/src/runtime/server/endpoint.ts7
-rw-r--r--packages/astro/test/units/runtime/endpoints.test.js49
4 files changed, 84 insertions, 0 deletions
diff --git a/.changeset/cuddly-suits-hunt.md b/.changeset/cuddly-suits-hunt.md
new file mode 100644
index 000000000..858c6529c
--- /dev/null
+++ b/.changeset/cuddly-suits-hunt.md
@@ -0,0 +1,5 @@
+---
+"astro": patch
+---
+
+Adds a helpful error message that will be shown when an endpoint does not return a `Response`.
diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts
index 67cd94b98..e59e62db4 100644
--- a/packages/astro/src/core/errors/errors-data.ts
+++ b/packages/astro/src/core/errors/errors-data.ts
@@ -791,6 +791,29 @@ export const MiddlewareNotAResponse = {
/**
* @docs
* @description
+ * Thrown when an endpoint does not return anything or returns an object that is not a `Response` object.
+ *
+ * An endpoint must return either a `Response`, or a `Promise` that resolves with a `Response`. For example:
+ * ```ts
+ * import type { APIContext } from 'astro';
+ *
+ * export async function GET({ request, url, cookies }: APIContext): Promise<Response> {
+ * return Response.json({
+ * success: true,
+ * result: 'Data from Astro Endpoint!'
+ * })
+ * }
+ * ```
+ */
+export const EndpointDidNotReturnAResponse = {
+ name: 'EndpointDidNotReturnAResponse',
+ title: 'The endpoint did not return a `Response`.',
+ message: 'An endpoint must return either a `Response`, or a `Promise` that resolves with a `Response`.',
+} satisfies ErrorData;
+
+/**
+ * @docs
+ * @description
*
* Thrown when `locals` is overwritten with something that is not an object
*
diff --git a/packages/astro/src/runtime/server/endpoint.ts b/packages/astro/src/runtime/server/endpoint.ts
index 6b6f3be26..7fa3150b3 100644
--- a/packages/astro/src/runtime/server/endpoint.ts
+++ b/packages/astro/src/runtime/server/endpoint.ts
@@ -2,6 +2,8 @@ import { bold } from 'kleur/colors';
import type { APIContext, EndpointHandler } from '../../@types/astro.js';
import { REROUTABLE_STATUS_CODES, REROUTE_DIRECTIVE_HEADER } from '../../core/constants.js';
import type { Logger } from '../../core/logger/core.js';
+import { AstroError } from '../../core/errors/errors.js';
+import { EndpointDidNotReturnAResponse } from '../../core/errors/errors-data.js';
/** Renders an endpoint request to completion, returning the body. */
export async function renderEndpoint(
@@ -49,6 +51,11 @@ export async function renderEndpoint(
}
const response = await handler.call(mod, context);
+
+ if (!response || response instanceof Response === false) {
+ throw new AstroError(EndpointDidNotReturnAResponse)
+ }
+
// Endpoints explicitly returning 404 or 500 response status should
// NOT be subject to rerouting to 404.astro or 500.astro.
if (REROUTABLE_STATUS_CODES.includes(response.status)) {
diff --git a/packages/astro/test/units/runtime/endpoints.test.js b/packages/astro/test/units/runtime/endpoints.test.js
new file mode 100644
index 000000000..47f52a2df
--- /dev/null
+++ b/packages/astro/test/units/runtime/endpoints.test.js
@@ -0,0 +1,49 @@
+import * as assert from 'node:assert/strict';
+import { after, before, describe, it } from 'node:test';
+import { fileURLToPath } from 'node:url';
+import { createContainer } from '../../../dist/core/dev/container.js';
+import testAdapter from '../../test-adapter.js';
+import {
+ createBasicSettings,
+ createFs,
+ createRequestAndResponse,
+ defaultLogger,
+} from '../test-utils.js';
+
+const root = new URL('../../fixtures/api-routes/', import.meta.url);
+const fileSystem = {
+ '/src/pages/incorrect.ts': `export const GET = _ => {}`,
+};
+
+describe('endpoints', () => {
+ let container;
+ let settings;
+
+ before(async () => {
+ const fs = createFs(fileSystem, root);
+ settings = await createBasicSettings({
+ root: fileURLToPath(root),
+ output: 'server',
+ adapter: testAdapter(),
+ });
+ container = await createContainer({
+ fs,
+ settings,
+ logger: defaultLogger,
+ });
+ });
+
+ after(async () => {
+ await container.close();
+ });
+
+ it('should respond with 500 for incorrect implementation', async () => {
+ const { req, res, done } = createRequestAndResponse({
+ method: 'GET',
+ url: '/incorrect',
+ });
+ container.handle(req, res);
+ await done;
+ assert.equal(res.statusCode, 500);
+ });
+});