Skip to content

Commit

Permalink
Merge branch 'refactor/create-api' into fs-router-api
Browse files Browse the repository at this point in the history
  • Loading branch information
tylersayshi committed Jan 13, 2025
2 parents 0bb2660 + cca3b8f commit 7aa00b5
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,20 +408,27 @@ export const createPages = <
}
};

const createApi: CreateApi = ({ path, mode, method, handler }) => {
const createApi: CreateApi = (options) => {
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 @@ -640,7 +646,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 @@ -650,6 +656,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 7aa00b5

Please sign in to comment.