diff --git a/src/create-with-winter-spec.ts b/src/create-with-winter-spec.ts index 229a28c..bc1c4f6 100644 --- a/src/create-with-winter-spec.ts +++ b/src/create-with-winter-spec.ts @@ -126,6 +126,7 @@ function serializeResponse( ): Middleware { return async (req, ctx, next) => { const rawResponse = await next(req, ctx) + assertResponseWasReturned(rawResponse) const statusCode = rawResponse instanceof WinterSpecResponse @@ -150,6 +151,18 @@ function serializeResponse( } } +function assertResponseWasReturned( + rawResponse: unknown +): asserts rawResponse is SerializableToResponse | Response { + if (rawResponse == null || typeof rawResponse !== "object") { + throw new Error( + `Use ctx.json({...}) or return a Response instead of returning ${String( + rawResponse + )} directly.` + ) + } +} + export async function wrapMiddlewares( middlewares: MiddlewareChain, routeFn: WinterSpecRouteFn, diff --git a/tests/errors/do-not-allow-raw-json.test.ts b/tests/errors/do-not-allow-raw-json.test.ts index 8682796..62ae100 100644 --- a/tests/errors/do-not-allow-raw-json.test.ts +++ b/tests/errors/do-not-allow-raw-json.test.ts @@ -37,3 +37,31 @@ test("should throw an error when responding with raw JSON", async (t) => { ) ) }) + +test("should throw a helpful error when route returns undefined", async (t) => { + const { axios } = await getTestRoute(t, { + globalSpec: { + authMiddleware: {}, + beforeAuthMiddleware: [ + async (req, ctx, next) => { + try { + return await next(req, ctx) + } catch (e: any) { + return Response.json({ error: e.message }, { status: 500 }) + } + }, + ], + }, + routeSpec: { + methods: ["GET"], + jsonResponse: z.any(), + }, + routePath: "/", + routeFn: (() => undefined) as any, + }) + + const { data } = await axios.get("/", { + validateStatus: () => true, + }) + t.true(data.error.includes("returning undefined directly")) +})