diff --git a/packages/better-call/src/router.test.ts b/packages/better-call/src/router.test.ts index 599b8f4..4ba4cd9 100644 --- a/packages/better-call/src/router.test.ts +++ b/packages/better-call/src/router.test.ts @@ -620,6 +620,73 @@ describe("error handling", () => { expect(body.message).toBe("Resource not found"); }); + describe("invalid JSON body", () => { + it("should return 400 for empty JSON body", async () => { + const endpoint = createEndpoint( + "/post", + { + method: "POST", + }, + async (c) => { + return c.body; + }, + ); + const router = createRouter({ endpoint }); + const request = new Request("http://localhost/post", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "", + }); + const response = await router.handler(request); + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.code).toBe("BAD_REQUEST"); + expect(body.message).toBe("Invalid JSON in request body"); + }); + + it("should return 400 for malformed JSON body", async () => { + const endpoint = createEndpoint( + "/post", + { + method: "POST", + }, + async (c) => { + return c.body; + }, + ); + const router = createRouter({ endpoint }); + const request = new Request("http://localhost/post", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "not json", + }); + const response = await router.handler(request); + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.code).toBe("BAD_REQUEST"); + }); + + it("should return 200 for valid JSON body", async () => { + const endpoint = createEndpoint( + "/post", + { + method: "POST", + }, + async (c) => { + return c.body; + }, + ); + const router = createRouter({ endpoint }); + const request = new Request("http://localhost/post", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "{}", + }); + const response = await router.handler(request); + expect(response.status).toBe(200); + }); + }); + describe("allowedMediaTypes", () => { it("should allow requests with allowed media type at router level", async () => { const endpoint = createEndpoint( diff --git a/packages/better-call/src/utils.ts b/packages/better-call/src/utils.ts index 8506d40..383eb8d 100644 --- a/packages/better-call/src/utils.ts +++ b/packages/better-call/src/utils.ts @@ -39,7 +39,17 @@ export async function getBody(request: Request, allowedMediaTypes?: string[]) { } if (jsonContentTypeRegex.test(normalizedContentType)) { - return await request.json(); + try { + return await request.json(); + } catch (e) { + if (e instanceof SyntaxError) { + throw new APIError(400, { + message: "Invalid JSON in request body", + code: "BAD_REQUEST", + }); + } + throw e; + } } if (normalizedContentType.includes("application/x-www-form-urlencoded")) {