Skip to content

Commit

Permalink
refactor(create-pages): dynamic handlers and static handler (#1147)
Browse files Browse the repository at this point in the history
refactor createApi to support multiple handlers on dynamic route with
single createApi call

this also enforces with types static paths only have a single handler

Co-authored-by: Tyler <[email protected]>
  • Loading branch information
tylersayshi and tylersayshi authored Jan 13, 2025
1 parent 42f9000 commit 00a5065
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 59 deletions.
21 changes: 8 additions & 13 deletions e2e/fixtures/create-pages/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,14 @@ const pages: ReturnType<typeof createPages> = createPages(
createApi({
path: '/api/hi',
mode: 'dynamic',
method: 'GET',
handler: async () => {
return new Response('hello world!');
},
}),

createApi({
path: '/api/hi',
mode: 'dynamic',
method: 'POST',
handler: async (req) => {
const body = await req.text();
return new Response(`POST to hello world! ${body}`);
handlers: {
GET: async () => {
return new Response('hello world!');
},
POST: async (req) => {
const body = await req.text();
return new Response(`POST to hello world! ${body}`);
},
},
}),

Expand Down
21 changes: 8 additions & 13 deletions examples/21_create-pages/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,14 @@ const pages = createPages(
createApi({
path: '/api/hi',
mode: 'dynamic',
method: 'GET',
handler: async () => {
return new Response('hello world!');
},
}),

createApi({
path: '/api/hi',
method: 'POST',
mode: 'dynamic',
handler: async (req) => {
const name = await req.text();
return new Response(`hello ${name}!`);
handlers: {
GET: async () => {
return new Response('hello world!');
},
POST: async (req) => {
const name = await req.text();
return new Response(`hello ${name}!`);
},
},
}),

Expand Down
52 changes: 32 additions & 20 deletions packages/waku/src/router/create-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,20 @@ export type CreateLayout = <Path extends string>(

type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';

type ApiHandler = (req: Request) => Promise<Response>;

export type CreateApi = <Path extends string>(
params:
| {
path: Path;
mode: 'static';
path: Path;
method: 'GET';
handler: (req: Request) => Promise<Response>;
handler: ApiHandler;
}
| {
path: Path;
mode: 'dynamic';
method: Method;
handler: (req: Request) => Promise<Response>;
path: Path;
handlers: Partial<Record<Method, ApiHandler>>;
},
) => void;

Expand Down Expand Up @@ -240,10 +241,9 @@ export const createPages = <
{
mode: 'static' | 'dynamic';
pathSpec: PathSpec;
handler: Parameters<CreateApi>[0]['handler'];
handlers: Partial<Record<Method, ApiHandler>>;
}
>();
const staticApiPaths = new Set<string>();
const staticComponentMap = new Map<string, FunctionComponent<any>>();
let rootItem: RootItem | undefined = undefined;
const noSsrSet = new WeakSet<PathSpec>();
Expand All @@ -268,9 +268,8 @@ export const createPages = <
path: string,
method: string,
) => string | undefined = (path, method) => {
for (const pathKey of apiPathMap.keys()) {
const [m, p] = pathKey.split(' ');
if (m === method && getPathMapping(parsePathWithSlug(p!), path)) {
for (const [p, v] of apiPathMap.entries()) {
if (method in v.handlers && getPathMapping(parsePathWithSlug(p!), path)) {
return p;
}
}
Expand Down Expand Up @@ -409,24 +408,31 @@ export const createPages = <
}
};

const createApi: CreateApi = ({ path, mode, method, handler }) => {
const createApi: CreateApi = (options) => {
if (!import.meta.env.VITE_EXPERIMENTAL_WAKU_ROUTER) {
console.warn('createApi is still experimental');
return;
}
if (configured) {
throw new Error('createApi no longer available');
}
if (apiPathMap.has(`${method} ${path}`)) {
throw new Error(`Duplicated api path+method: ${path} ${method}`);
} else if (mode === 'static' && staticApiPaths.has(path)) {
throw new Error('Static API Routes cannot share paths: ' + path);
if (apiPathMap.has(options.path)) {
throw new Error(`Duplicated api path: ${options.path}`);
}
if (mode === 'static') {
staticApiPaths.add(path);
const pathSpec = parsePathWithSlug(options.path);
if (options.mode === 'static') {
apiPathMap.set(options.path, {
mode: 'static',
pathSpec,
handlers: { GET: options.handler },
});
} else {
apiPathMap.set(options.path, {
mode: 'dynamic',
pathSpec,
handlers: options.handlers,
});
}
const pathSpec = parsePathWithSlug(path);
apiPathMap.set(`${method} ${path}`, { mode, pathSpec, handler });
};

const createRoot: CreateRoot = (root) => {
Expand Down Expand Up @@ -644,7 +650,7 @@ export const createPages = <
if (!routePath) {
throw new Error('API Route not found: ' + path);
}
const { handler } = apiPathMap.get(`${options.method} ${routePath}`)!;
const { handlers } = apiPathMap.get(routePath)!;

const req = new Request(
new URL(
Expand All @@ -654,6 +660,12 @@ export const createPages = <
),
options,
);
const handler = handlers[options.method as Method];
if (!handler) {
throw new Error(
'API method not found: ' + options.method + 'for path: ' + path,
);
}
const res = await handler(req);

return {
Expand Down
29 changes: 16 additions & 13 deletions packages/waku/tests/create-pages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,10 @@ describe('type tests', () => {
createApi({
path: '/foo',
mode: 'dynamic',
// @ts-expect-error: method is not valid
method: 'foo',
// @ts-expect-error: null is not valid
handler: () => null,
handlers: {
// @ts-expect-error: null is not valid
GET: () => null,
},
});
// @ts-expect-error: handler is not valid
createApi({ path: '/', mode: 'dynamic', method: 'GET', handler: 123 });
Expand All @@ -310,9 +310,10 @@ describe('type tests', () => {
createApi({
path: '/foo/[slug]',
mode: 'dynamic',
method: 'GET',
handler: async () => {
return new Response('Hello World');
handlers: {
POST: async (req) => {
return new Response('Hello World ' + new URL(req.url).pathname);
},
},
});
});
Expand Down Expand Up @@ -578,9 +579,10 @@ describe('createPages pages and layouts', () => {
createApi({
path: '/test/[slug]',
mode: 'dynamic',
method: 'GET',
handler: async () => {
return new Response('Hello World');
handlers: {
GET: async () => {
return new Response('Hello World');
},
},
}),
]);
Expand Down Expand Up @@ -1340,9 +1342,10 @@ describe('createPages api', () => {
createApi({
path: '/test/[slug]',
mode: 'dynamic',
method: 'GET',
handler: async (req) => {
return new Response('Hello World ' + req.url.split('/').at(-1)!);
handlers: {
GET: async (req) => {
return new Response('Hello World ' + req.url.split('/').at(-1)!);
},
},
}),
]);
Expand Down

0 comments on commit 00a5065

Please sign in to comment.