From 6643a3931d45e63363599bb0cf7b2b2951266cfb Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Wed, 4 May 2022 14:55:37 -0400 Subject: [PATCH] Conform to API route signature (#3272) * Conform to API route signature * Rename to API route * Update ssr test * Update packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js Co-authored-by: Ben Holmes * Adds a changeset * Make PR review changes Co-authored-by: Ben Holmes --- .changeset/fast-dolls-eat.md | 5 +++ packages/astro/src/@types/astro.ts | 17 ++++++- packages/astro/src/core/render/route-cache.ts | 1 + packages/astro/src/runtime/server/index.ts | 42 +++++++++++++++++- packages/astro/test/api-routes.test.js | 44 +++++++++++++++++++ .../src/pages/context/data/[param].json.js | 24 ++++++++++ .../src/pages/old-api/onearg/[param].json.js | 23 ++++++++++ .../src/pages/old-api/twoarg/[param].json.js | 24 ++++++++++ .../src/pages/api/products/[id].js | 2 +- 9 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 .changeset/fast-dolls-eat.md create mode 100644 packages/astro/test/api-routes.test.js create mode 100644 packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js create mode 100644 packages/astro/test/fixtures/api-routes/src/pages/old-api/onearg/[param].json.js create mode 100644 packages/astro/test/fixtures/api-routes/src/pages/old-api/twoarg/[param].json.js diff --git a/.changeset/fast-dolls-eat.md b/.changeset/fast-dolls-eat.md new file mode 100644 index 000000000..3eda1c985 --- /dev/null +++ b/.changeset/fast-dolls-eat.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Implements the Dynamic Route API RFC diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 2848760d2..805eeb407 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -856,12 +856,27 @@ export interface AstroAdapter { args?: any; } +export interface APIContext { + params: Params; + request: Request; +} + export interface EndpointOutput { body: Output; } +interface APIRoute { + (context: APIContext): EndpointOutput | Response; + + /** + * @deprecated + * Use { context: APIRouteContext } object instead. + */ + (params: Params, request: Request): EndpointOutput | Response; +} + export interface EndpointHandler { - [method: string]: (params: any, request: Request) => EndpointOutput | Response; + [method: string]: APIRoute; } export interface AstroRenderer { diff --git a/packages/astro/src/core/render/route-cache.ts b/packages/astro/src/core/render/route-cache.ts index b564a6ca6..762128555 100644 --- a/packages/astro/src/core/render/route-cache.ts +++ b/packages/astro/src/core/render/route-cache.ts @@ -52,6 +52,7 @@ export async function callGetStaticPaths({ const keyedStaticPaths = staticPaths as GetStaticPathsResultKeyed; keyedStaticPaths.keyed = new Map(); + for (const sp of keyedStaticPaths) { const paramsKey = stringifyParams(sp.params); keyedStaticPaths.keyed.set(paramsKey, sp); diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index c9df2e62b..dd278ae5a 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -1,4 +1,5 @@ import type { + APIContext, AstroComponentMetadata, AstroGlobalPartial, EndpointHandler, @@ -468,7 +469,46 @@ export async function renderEndpoint(mod: EndpointHandler, request: Request, par `Endpoint handler not found! Expected an exported function for "${chosenMethod}"` ); } - return await handler.call(mod, params, request); + + if(handler.length > 1) { + // eslint-disable-next-line no-console + console.warn(` +API routes with 2 arguments have been deprecated. Instead they take a single argument in the form of: + +export function get({ params, request }) { + //... +} + +Update your code to remove this warning.`) + } + + const context = { + request, + params + }; + + const proxy = new Proxy(context, { + get(target, prop) { + if(prop in target) { + return Reflect.get(target, prop); + } else if(prop in params) { + // eslint-disable-next-line no-console + console.warn(` +API routes no longer pass params as the first argument. Instead an object containing a params property is provided in the form of: + +export function get({ params }) { + // ... +} + +Update your code to remove this warning.`); + return Reflect.get(params, prop); + } else { + return undefined; + } + } + }) as APIContext & Params; + + return await handler.call(mod, proxy, request); } async function replaceHeadInjection(result: SSRResult, html: string): Promise { diff --git a/packages/astro/test/api-routes.test.js b/packages/astro/test/api-routes.test.js new file mode 100644 index 000000000..d7d0c3d29 --- /dev/null +++ b/packages/astro/test/api-routes.test.js @@ -0,0 +1,44 @@ +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; + +describe('API routes', () => { + let fixture; + + before(async () => { + fixture = await loadFixture({ root: './fixtures/api-routes/' }); + await fixture.build(); + }); + + describe('Deprecated API', () => { + it('two argument supported', async () => { + const one = JSON.parse(await fixture.readFile('/old-api/twoarg/one.json')); + expect(one).to.deep.equal({ + param: 'one', + pathname: '/old-api/twoarg/one.json' + }); + const two = JSON.parse(await fixture.readFile('/old-api/twoarg/two.json')); + expect(two).to.deep.equal({ + param: 'two', + pathname: '/old-api/twoarg/two.json' + }); + }); + + it('param first argument is supported', async () => { + const one = JSON.parse(await fixture.readFile('/old-api/onearg/one.json')); + expect(one).to.deep.equal({ + param: 'one' + }); + }); + }); + + describe('1.0 API', () => { + it('Receives a context argument', async () => { + const one = JSON.parse(await fixture.readFile('/context/data/one.json')); + expect(one).to.deep.equal({ + param: 'one', + pathname: '/context/data/one.json' + }); + }); + }); +}); diff --git a/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js b/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js new file mode 100644 index 000000000..2ed42a5ec --- /dev/null +++ b/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js @@ -0,0 +1,24 @@ + +export function getStaticPaths() { + return [ + { + params: { + param: 'one' + } + }, + { + params: { + param: 'two' + } + }, + ] +} + +export function get({ params, request }) { + return { + body: JSON.stringify({ + param: params.param, + pathname: new URL(request.url).pathname + }) + }; +} diff --git a/packages/astro/test/fixtures/api-routes/src/pages/old-api/onearg/[param].json.js b/packages/astro/test/fixtures/api-routes/src/pages/old-api/onearg/[param].json.js new file mode 100644 index 000000000..63771f8e0 --- /dev/null +++ b/packages/astro/test/fixtures/api-routes/src/pages/old-api/onearg/[param].json.js @@ -0,0 +1,23 @@ + +export function getStaticPaths() { + return [ + { + params: { + param: 'one' + } + }, + { + params: { + param: 'two' + } + }, + ] +} + +export function get(params) { + return { + body: JSON.stringify({ + param: params.param + }) + }; +} diff --git a/packages/astro/test/fixtures/api-routes/src/pages/old-api/twoarg/[param].json.js b/packages/astro/test/fixtures/api-routes/src/pages/old-api/twoarg/[param].json.js new file mode 100644 index 000000000..ab71d6896 --- /dev/null +++ b/packages/astro/test/fixtures/api-routes/src/pages/old-api/twoarg/[param].json.js @@ -0,0 +1,24 @@ + +export function getStaticPaths() { + return [ + { + params: { + param: 'one' + } + }, + { + params: { + param: 'two' + } + }, + ] +} + +export function get(params, request) { + return { + body: JSON.stringify({ + param: params.param, + pathname: new URL(request.url).pathname + }) + }; +} diff --git a/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js b/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js index 2d619ee74..4d96b62a5 100644 --- a/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js +++ b/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js @@ -1,5 +1,5 @@ -export function get(params) { +export function get({ params }) { return { body: JSON.stringify(params) };