Skip to content

Commit

Permalink
feat: improvements to API routes
Browse files Browse the repository at this point in the history
1. Make sure that API routes hot reload properly - Shopify/hydrogen#497
2. Support automatically creating Responses from strings and primitive JS objects - Shopify/hydrogen#476
  • Loading branch information
blittle committed Jan 24, 2022
1 parent e5000ff commit 2bff8ce
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 5 deletions.
25 changes: 20 additions & 5 deletions packages/hydrogen/src/utilities/apiRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {matchPath} from './matchPath';
import {Logger, logServerResponse} from '../utilities/log/log';
import {ServerComponentRequest} from '../framework/Hydration/ServerComponentRequest.server';

let cachedRoutes: Array<HydrogenApiRoute> = [];
let memoizedRoutes: Array<HydrogenApiRoute> = [];
let memoizedPages: ImportGlobEagerOutput = {};

type RouteParams = Record<string, string>;
type RequestOptions = {
Expand All @@ -12,7 +13,7 @@ type RequestOptions = {
type ResourceGetter = (
request: Request,
requestOptions: RequestOptions
) => Promise<Response>;
) => Promise<Response | Object | String>;

interface HydrogenApiRoute {
path: string;
Expand All @@ -30,7 +31,7 @@ export function getApiRoutesFromPages(
pages: ImportGlobEagerOutput | undefined,
topLevelPath = '*'
): Array<HydrogenApiRoute> {
if (cachedRoutes?.length || !pages) return cachedRoutes;
if (!pages || memoizedPages === pages) return memoizedRoutes;

const topLevelPrefix = topLevelPath.replace('*', '').replace(/\/$/, '');

Expand Down Expand Up @@ -71,12 +72,14 @@ export function getApiRoutesFromPages(
};
});

cachedRoutes = [
memoizedRoutes = [
...routes.filter((route) => !route.path.includes(':')),
...routes.filter((route) => route.path.includes(':')),
];

return cachedRoutes;
memoizedPages = pages;

return memoizedRoutes;
}

export function getApiRouteFromURL(
Expand Down Expand Up @@ -112,6 +115,18 @@ export async function renderApiRoute(

try {
response = await route.resource(request, {params: route.params});

if (!(response instanceof Response)) {
if (typeof response === 'string' || response instanceof String) {
response = new Response(response as string);
} else if (typeof response === 'object') {
response = new Response(JSON.stringify(response), {
headers: {
'Content-Type': 'application/json',
},
});
}
}
} catch (e) {
log.error(e);
response = new Response('Error processing: ' + request.url, {status: 500});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function api() {
return {some: 'json'};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function api() {
return 'some string';
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,22 @@ it('should return 404 on unknown method', async () => {
expect(text).toBe('Comment method not found');
});

it('should support simple strings returned from API routes', async () => {
const response = await page.request.get(url + '/string');
const text = await response.text();

expect(response.status()).toBe(200);
expect(text).toBe('some string');
});

it('should support objects as json returned from API routes', async () => {
const response = await page.request.get(url + '/json');
const json = await response.json();

expect(response.status()).toBe(200);
expect(json).toEqual({some: 'json'});
});

it('supports form request on API routes', async () => {
await page.goto(url + '/form');
await page.type('#fname', 'sometext');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function api() {
return {some: 'json'};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function api() {
return 'some string';
}
16 changes: 16 additions & 0 deletions packages/playground/server-components/tests/node-e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,22 @@ it('should return 404 on unknown method', async () => {
expect(text).toBe('Comment method not found');
});

it('should support simple strings returned from API routes', async () => {
const response = await page.request.get(viteTestUrl + '/string');
const text = await response.text();

expect(response.status()).toBe(200);
expect(text).toBe('some string');
});

it('should support objects as json returned from API routes', async () => {
const response = await page.request.get(viteTestUrl + '/json');
const json = await response.json();

expect(response.status()).toBe(200);
expect(json).toEqual({some: 'json'});
});

it.skip('supports form request on API routes', async () => {
await page.goto(viteTestUrl + '/form');
await page.type('#fname', 'sometext');
Expand Down

0 comments on commit 2bff8ce

Please sign in to comment.