diff options
author | 2024-03-18 20:06:46 +0530 | |
---|---|---|
committer | 2024-03-18 20:06:46 +0530 | |
commit | c12666166db724915e42e37a048483c99f88e6d9 (patch) | |
tree | 35675c0f5a3def888865252e3b733db14ccb6e33 | |
parent | 7138aa46781bc2ee3fb17a2b2d2d01cf94560726 (diff) | |
download | astro-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.md | 5 | ||||
-rw-r--r-- | packages/astro/src/core/errors/errors-data.ts | 23 | ||||
-rw-r--r-- | packages/astro/src/runtime/server/endpoint.ts | 7 | ||||
-rw-r--r-- | packages/astro/test/units/runtime/endpoints.test.js | 49 |
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); + }); +}); |