Skip to content

Commit

Permalink
feat: object-syntax event handlers (#485)
Browse files Browse the repository at this point in the history
Co-authored-by: Pooya Parsa <[email protected]>
  • Loading branch information
danielroe and pi0 authored Aug 7, 2023
1 parent b3427f5 commit 7e95702
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 5 deletions.
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);
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

0 comments on commit 7e95702

Please sign in to comment.