diff --git a/src/event/utils.ts b/src/event/utils.ts
index 4ec7fb57..0a69fdb3 100644
--- a/src/event/utils.ts
+++ b/src/event/utils.ts
@@ -8,6 +8,9 @@ import type {
_ResponseMiddleware,
} from "../types";
import { hasProp } from "../utils/internal/object";
+import { validateData } from "../utils/internal/validate";
+import { readBody } from "../utils/body";
+import { getQuery } from "../utils/request";
import type { H3Event } from "./event";
type _EventHandlerHooks = {
@@ -16,12 +19,17 @@ type _EventHandlerHooks = {
};
export function defineEventHandler<
- Request extends EventHandlerRequest = EventHandlerRequest,
+ Body extends EventHandlerRequest["body"] = EventHandlerRequest["body"],
+ Query extends EventHandlerRequest["query"] = EventHandlerRequest["query"],
+ Request extends EventHandlerRequest
= EventHandlerRequest<
+ Body,
+ Query
+ >,
Response = EventHandlerResponse,
>(
handler:
| EventHandler
- | EventHandlerObject,
+ | EventHandlerObject,
): EventHandler;
// TODO: remove when appropriate
// This signature provides backwards compatibility with previous signature where first generic was return type
@@ -38,12 +46,17 @@ export function defineEventHandler<
Request extends EventHandlerRequest ? Response : Request
>;
export function defineEventHandler<
- Request extends EventHandlerRequest,
+ Body extends EventHandlerRequest["body"] = EventHandlerRequest["body"],
+ Query extends EventHandlerRequest["query"] = EventHandlerRequest["query"],
+ Request extends EventHandlerRequest = EventHandlerRequest<
+ Body,
+ Query
+ >,
Response = EventHandlerResponse,
>(
handler:
| EventHandler
- | EventHandlerObject,
+ | EventHandlerObject,
): EventHandler {
// Function Syntax
if (typeof handler === "function") {
@@ -55,7 +68,15 @@ export function defineEventHandler<
onRequest: _normalizeArray(handler.onRequest),
onBeforeResponse: _normalizeArray(handler.onBeforeResponse),
};
- const _handler: EventHandler = (event) => {
+ const _handler: EventHandler = async (event) => {
+ if (handler.bodyValidator) {
+ const body = await readBody(event);
+ await validateData(body, handler.bodyValidator);
+ }
+ if (handler.queryValidator) {
+ const query = getQuery(event);
+ await validateData(query, handler.queryValidator);
+ }
return _callHandler(event, handler.handler, _hooks);
};
_handler.__is_handler__ = true;
diff --git a/src/types.ts b/src/types.ts
index 7116c475..61e03ded 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -3,6 +3,7 @@ import type { Hooks as WSHooks } from "crossws";
import type { H3Event } from "./event";
import type { Session } from "./utils/session";
import type { RouteNode } from "./router";
+import type { ValidateFunction } from "./types";
export type {
ValidateFunction,
@@ -51,9 +52,12 @@ export interface H3EventContext extends Record {
export type EventHandlerResponse = T | Promise;
-export interface EventHandlerRequest {
- body?: any; // TODO: Default to unknown in next major version
- query?: QueryObject;
+export interface EventHandlerRequest<
+ Body = any, // TODO: Default to unknown in next major version
+ Query extends QueryObject | undefined = QueryObject | undefined,
+> {
+ body?: Body;
+ query?: Query;
routerParams?: Record;
}
@@ -92,7 +96,12 @@ export type _ResponseMiddleware<
) => void | Promise;
export type EventHandlerObject<
- Request extends EventHandlerRequest = EventHandlerRequest,
+ Body extends EventHandlerRequest["body"] = EventHandlerRequest["body"],
+ Query extends EventHandlerRequest["query"] = EventHandlerRequest["query"],
+ Request extends EventHandlerRequest = EventHandlerRequest<
+ Body,
+ Query
+ >,
Response extends EventHandlerResponse = EventHandlerResponse,
> = {
onRequest?: _RequestMiddleware | _RequestMiddleware[];
@@ -101,6 +110,8 @@ export type EventHandlerObject<
| _ResponseMiddleware[];
/** @experimental */
websocket?: Partial;
+ bodyValidator?: ValidateFunction;
+ queryValidator?: ValidateFunction;
handler: EventHandler;
};
diff --git a/src/utils/body.ts b/src/utils/body.ts
index 3f71b16c..eb0ebf38 100644
--- a/src/utils/body.ts
+++ b/src/utils/body.ts
@@ -142,7 +142,7 @@ export function readRawBody(
export async function readBody<
T,
Event extends H3Event = H3Event,
- _T = InferEventInput<"body", Event, T>,
+ _T = Exclude, undefined>,
>(event: Event, options: { strict?: boolean } = {}): Promise<_T> {
const request = event.node.req as InternalRequest;
if (hasProp(request, ParsedBodySymbol)) {
diff --git a/test/types.test-d.ts b/test/types.test-d.ts
index 9160b0ef..3f97365b 100644
--- a/test/types.test-d.ts
+++ b/test/types.test-d.ts
@@ -34,6 +34,7 @@ describe("types", () => {
foo: string;
}>();
});
+
it("return type (inferred)", () => {
const handler = eventHandler(() => {
return {
@@ -45,7 +46,7 @@ describe("types", () => {
});
it("return type (simple generic)", () => {
- const handler = eventHandler(() => {
+ const handler = eventHandler(() => {
return "";
});
const response = handler({} as H3Event);
@@ -77,10 +78,19 @@ describe("types", () => {
expectTypeOf(body).not.toBeAny();
expectTypeOf(body).toEqualTypeOf<{ id: string }>();
});
+
+ eventHandler({
+ bodyValidator: (body: unknown) => body as { id: string },
+ handler: async (event) => {
+ const body = await readBody(event);
+ expectTypeOf(body).not.toBeAny();
+ expectTypeOf(body).toEqualTypeOf<{ id: string }>();
+ }
+ });
});
it("typed via event handler", () => {
- eventHandler<{ body: { id: string } }>(async (event) => {
+ eventHandler<{ id: string }>(async (event) => {
const body = await readBody(event);
expectTypeOf(body).not.toBeAny();
expectTypeOf(body).toEqualTypeOf<{ id: string }>();
@@ -107,15 +117,24 @@ describe("types", () => {
it("typed via validator", () => {
eventHandler(async (event) => {
- const validator = (body: unknown) => body as { id: string };
- const body = await getValidatedQuery(event, validator);
- expectTypeOf(body).not.toBeAny();
- expectTypeOf(body).toEqualTypeOf<{ id: string }>();
+ const validator = (query: unknown) => query as { id: string };
+ const query = await getValidatedQuery(event, validator);
+ expectTypeOf(query).not.toBeAny();
+ expectTypeOf(query).toEqualTypeOf<{ id: string }>();
+ });
+
+ eventHandler({
+ queryValidator: (query: unknown) => query as { id: string },
+ handler: (event) => {
+ const query = getQuery(event);
+ expectTypeOf(query).not.toBeAny();
+ expectTypeOf(query).toEqualTypeOf<{ id: string }>();
+ }
});
});
it("typed via event handler", () => {
- eventHandler<{ query: { id: string } }>((event) => {
+ eventHandler((event) => {
const query = getQuery(event);
expectTypeOf(query).not.toBeAny();
expectTypeOf(query).toEqualTypeOf<{ id: string }>();