Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: object-syntax event handlers #485

Merged
merged 13 commits into from
Aug 7, 2023
47 changes: 42 additions & 5 deletions src/event/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import type {
LazyEventHandler,
EventHandlerRequest,
EventHandlerResponse,
EventHandlerObject,
} from "../types";
import type { H3Event } from "./event";

export function defineEventHandler<
Request extends EventHandlerRequest = EventHandlerRequest,
Response = any,
>(handler: EventHandler<Request, Response>): EventHandler<Request, Response>;
Response = EventHandlerResponse,
>(
handler:
| EventHandler<Request, Response>
| EventHandlerObject<Request, Response>,
): EventHandler<Request, Response>;
// TODO: remove when appropriate
// This signature provides backwards compatibility with previous signature where first generic was return type
export function defineEventHandler<
Expand All @@ -26,10 +32,41 @@ export function defineEventHandler<
export function defineEventHandler<
Request extends EventHandlerRequest = EventHandlerRequest,
Response = EventHandlerResponse,
>(handler: EventHandler<Request, Response>): EventHandler<Request, Response> {
handler.__is_handler__ = true;
return handler;
>(
handler:
| EventHandler<Request, Response>
| EventHandlerObject<Request, Response>,
): EventHandler<Request, Response> {
// Function Syntax
if (typeof handler === "function") {
return Object.assign(handler, { __is_handler__: true });
}
// Object Syntax
const _handler: EventHandler<Request, any> = (event) => {
return _callHandler(event, handler);
};
return Object.assign(_handler, { __is_handler__: true });
}

async function _callHandler(event: H3Event, handler: EventHandlerObject) {
if (handler.before) {
for (const hook of handler.before) {
await hook(event);
pi0 marked this conversation as resolved.
Show resolved Hide resolved
if (event.handled) {
return;
}
}
}
const body = await handler.handler(event);
const response = { body };
if (handler.after) {
for (const hook of handler.after) {
await hook(event, response);
}
}
return response.body;
}

export const eventHandler = defineEventHandler;

export function isEventHandler(input: any): input is EventHandler {
Expand Down
12 changes: 12 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ export interface EventHandler<
(event: H3Event<Request>): Response;
}

export type EventHandlerObject<
Request extends EventHandlerRequest = EventHandlerRequest,
Response extends EventHandlerResponse = EventHandlerResponse,
> = {
handler: EventHandler<Request, Response>;
before?: ((event: H3Event<Request>) => void | Promise<void>)[];
after?: ((
event: H3Event<Request>,
response: { body?: Response },
) => void | Promise<void>)[];
};

export type LazyEventHandler = () => EventHandler | Promise<EventHandler>;

export type RequestHeaders = { [name: string]: string | undefined };
23 changes: 23 additions & 0 deletions test/types.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@ import {

describe("types", () => {
describe("eventHandler", () => {
it("object syntax definitions", async () => {
const handler = eventHandler({
before: [
(event) => {
expectTypeOf(event).toEqualTypeOf<H3Event>();
},
],
async handler(event) {
expectTypeOf(event).toEqualTypeOf<H3Event>();

const body = await readBody(event);
// TODO: Default to unknown in next major version
expectTypeOf(body).toBeAny();

return {
foo: "bar",
};
},
});
expectTypeOf(await handler({} as H3Event)).toEqualTypeOf<{
foo: string;
}>();
});
it("return type (inferred)", () => {
const handler = eventHandler(() => {
return {
Expand Down