Skip to content

Commit 3792394

Browse files
authored
Always await wrapResponse since it returns a Promise, to protect against UnhandledRejection errors (#1494)
1 parent 4305a23 commit 3792394

File tree

1 file changed

+67
-44
lines changed

1 file changed

+67
-44
lines changed

apps/webapp/app/services/routeBuilders/apiBuilder.server.ts

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function createLoaderApiRoute<
7878
const authenticationResult = await authenticateApiRequest(request, { allowJWT });
7979

8080
if (!authenticationResult) {
81-
return wrapResponse(
81+
return await wrapResponse(
8282
request,
8383
json({ error: "Invalid or Missing API key" }, { status: 401 }),
8484
corsStrategy !== "none"
@@ -89,7 +89,7 @@ export function createLoaderApiRoute<
8989
if (paramsSchema) {
9090
const parsed = paramsSchema.safeParse(params);
9191
if (!parsed.success) {
92-
return wrapResponse(
92+
return await wrapResponse(
9393
request,
9494
json(
9595
{ error: "Params Error", details: fromZodError(parsed.error).details },
@@ -106,7 +106,7 @@ export function createLoaderApiRoute<
106106
const searchParams = Object.fromEntries(new URL(request.url).searchParams);
107107
const parsed = searchParamsSchema.safeParse(searchParams);
108108
if (!parsed.success) {
109-
return wrapResponse(
109+
return await wrapResponse(
110110
request,
111111
json(
112112
{ error: "Query Error", details: fromZodError(parsed.error).details },
@@ -123,7 +123,7 @@ export function createLoaderApiRoute<
123123
const rawHeaders = Object.fromEntries(request.headers);
124124
const headers = headersSchema.safeParse(rawHeaders);
125125
if (!headers.success) {
126-
return wrapResponse(
126+
return await wrapResponse(
127127
request,
128128
json(
129129
{ error: "Headers Error", details: fromZodError(headers.error).details },
@@ -154,7 +154,7 @@ export function createLoaderApiRoute<
154154
);
155155

156156
if (!authorizationResult.authorized) {
157-
return wrapResponse(
157+
return await wrapResponse(
158158
request,
159159
json(
160160
{
@@ -177,16 +177,22 @@ export function createLoaderApiRoute<
177177
authentication: authenticationResult,
178178
request,
179179
});
180-
return wrapResponse(request, result, corsStrategy !== "none");
180+
return await wrapResponse(request, result, corsStrategy !== "none");
181181
} catch (error) {
182-
if (error instanceof Response) {
183-
return wrapResponse(request, error, corsStrategy !== "none");
182+
try {
183+
if (error instanceof Response) {
184+
return await wrapResponse(request, error, corsStrategy !== "none");
185+
}
186+
return await wrapResponse(
187+
request,
188+
json({ error: "Internal Server Error" }, { status: 500 }),
189+
corsStrategy !== "none"
190+
);
191+
} catch (innerError) {
192+
logger.error("[apiBuilder] Failed to handle error", { error, innerError });
193+
194+
return json({ error: "Internal Server Error" }, { status: 500 });
184195
}
185-
return wrapResponse(
186-
request,
187-
json({ error: "Internal Server Error" }, { status: 500 }),
188-
corsStrategy !== "none"
189-
);
190196
}
191197
};
192198
}
@@ -240,7 +246,7 @@ export function createLoaderPATApiRoute<
240246
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
241247

242248
if (!authenticationResult) {
243-
return wrapResponse(
249+
return await wrapResponse(
244250
request,
245251
json({ error: "Invalid or Missing API key" }, { status: 401 }),
246252
corsStrategy !== "none"
@@ -251,7 +257,7 @@ export function createLoaderPATApiRoute<
251257
if (paramsSchema) {
252258
const parsed = paramsSchema.safeParse(params);
253259
if (!parsed.success) {
254-
return wrapResponse(
260+
return await wrapResponse(
255261
request,
256262
json(
257263
{ error: "Params Error", details: fromZodError(parsed.error).details },
@@ -268,7 +274,7 @@ export function createLoaderPATApiRoute<
268274
const searchParams = Object.fromEntries(new URL(request.url).searchParams);
269275
const parsed = searchParamsSchema.safeParse(searchParams);
270276
if (!parsed.success) {
271-
return wrapResponse(
277+
return await wrapResponse(
272278
request,
273279
json(
274280
{ error: "Query Error", details: fromZodError(parsed.error).details },
@@ -285,7 +291,7 @@ export function createLoaderPATApiRoute<
285291
const rawHeaders = Object.fromEntries(request.headers);
286292
const headers = headersSchema.safeParse(rawHeaders);
287293
if (!headers.success) {
288-
return wrapResponse(
294+
return await wrapResponse(
289295
request,
290296
json(
291297
{ error: "Headers Error", details: fromZodError(headers.error).details },
@@ -304,17 +310,22 @@ export function createLoaderPATApiRoute<
304310
authentication: authenticationResult,
305311
request,
306312
});
307-
return wrapResponse(request, result, corsStrategy !== "none");
313+
return await wrapResponse(request, result, corsStrategy !== "none");
308314
} catch (error) {
309-
console.error("Error in API route:", error);
310-
if (error instanceof Response) {
311-
return wrapResponse(request, error, corsStrategy !== "none");
315+
try {
316+
if (error instanceof Response) {
317+
return await wrapResponse(request, error, corsStrategy !== "none");
318+
}
319+
return await wrapResponse(
320+
request,
321+
json({ error: "Internal Server Error" }, { status: 500 }),
322+
corsStrategy !== "none"
323+
);
324+
} catch (innerError) {
325+
logger.error("[apiBuilder] Failed to handle error", { error, innerError });
326+
327+
return json({ error: "Internal Server Error" }, { status: 500 });
312328
}
313-
return wrapResponse(
314-
request,
315-
json({ error: "Internal Server Error" }, { status: 500 }),
316-
corsStrategy !== "none"
317-
);
318329
}
319330
};
320331
}
@@ -388,7 +399,7 @@ export function createActionApiRoute<
388399
const authenticationResult = await authenticateApiRequest(request, { allowJWT });
389400

390401
if (!authenticationResult) {
391-
return wrapResponse(
402+
return await wrapResponse(
392403
request,
393404
json({ error: "Invalid or Missing API key" }, { status: 401 }),
394405
corsStrategy !== "none"
@@ -407,7 +418,7 @@ export function createActionApiRoute<
407418
if (paramsSchema) {
408419
const parsed = paramsSchema.safeParse(params);
409420
if (!parsed.success) {
410-
return wrapResponse(
421+
return await wrapResponse(
411422
request,
412423
json(
413424
{ error: "Params Error", details: fromZodError(parsed.error).details },
@@ -424,7 +435,7 @@ export function createActionApiRoute<
424435
const searchParams = Object.fromEntries(new URL(request.url).searchParams);
425436
const parsed = searchParamsSchema.safeParse(searchParams);
426437
if (!parsed.success) {
427-
return wrapResponse(
438+
return await wrapResponse(
428439
request,
429440
json(
430441
{ error: "Query Error", details: fromZodError(parsed.error).details },
@@ -441,7 +452,7 @@ export function createActionApiRoute<
441452
const rawHeaders = Object.fromEntries(request.headers);
442453
const headers = headersSchema.safeParse(rawHeaders);
443454
if (!headers.success) {
444-
return wrapResponse(
455+
return await wrapResponse(
445456
request,
446457
json(
447458
{ error: "Headers Error", details: fromZodError(headers.error).details },
@@ -457,7 +468,7 @@ export function createActionApiRoute<
457468
if (bodySchema) {
458469
const rawBody = await request.text();
459470
if (rawBody.length === 0) {
460-
return wrapResponse(
471+
return await wrapResponse(
461472
request,
462473
json({ error: "Request body is empty" }, { status: 400 }),
463474
corsStrategy !== "none"
@@ -467,7 +478,7 @@ export function createActionApiRoute<
467478
const rawParsedJson = safeJsonParse(rawBody);
468479

469480
if (!rawParsedJson) {
470-
return wrapResponse(
481+
return await wrapResponse(
471482
request,
472483
json({ error: "Invalid JSON" }, { status: 400 }),
473484
corsStrategy !== "none"
@@ -476,7 +487,7 @@ export function createActionApiRoute<
476487

477488
const body = bodySchema.safeParse(rawParsedJson);
478489
if (!body.success) {
479-
return wrapResponse(
490+
return await wrapResponse(
480491
request,
481492
json({ error: fromZodError(body.error).toString() }, { status: 400 }),
482493
corsStrategy !== "none"
@@ -497,7 +508,7 @@ export function createActionApiRoute<
497508
});
498509

499510
if (!checkAuthorization(authenticationResult, action, $resource, superScopes)) {
500-
return wrapResponse(
511+
return await wrapResponse(
501512
request,
502513
json({ error: "Unauthorized" }, { status: 403 }),
503514
corsStrategy !== "none"
@@ -513,24 +524,36 @@ export function createActionApiRoute<
513524
authentication: authenticationResult,
514525
request,
515526
});
516-
return wrapResponse(request, result, corsStrategy !== "none");
527+
return await wrapResponse(request, result, corsStrategy !== "none");
517528
} catch (error) {
518-
if (error instanceof Response) {
519-
return wrapResponse(request, error, corsStrategy !== "none");
529+
try {
530+
if (error instanceof Response) {
531+
return await wrapResponse(request, error, corsStrategy !== "none");
532+
}
533+
return await wrapResponse(
534+
request,
535+
json({ error: "Internal Server Error" }, { status: 500 }),
536+
corsStrategy !== "none"
537+
);
538+
} catch (innerError) {
539+
logger.error("[apiBuilder] Failed to handle error", { error, innerError });
540+
541+
return json({ error: "Internal Server Error" }, { status: 500 });
520542
}
521-
return wrapResponse(
522-
request,
523-
json({ error: "Internal Server Error" }, { status: 500 }),
524-
corsStrategy !== "none"
525-
);
526543
}
527544
}
528545

529546
return { loader, action };
530547
}
531548

532-
function wrapResponse(request: Request, response: Response, useCors: boolean) {
549+
async function wrapResponse(
550+
request: Request,
551+
response: Response,
552+
useCors: boolean
553+
): Promise<Response> {
533554
return useCors
534-
? apiCors(request, response, { exposedHeaders: ["x-trigger-jwt", "x-trigger-jwt-claims"] })
555+
? await apiCors(request, response, {
556+
exposedHeaders: ["x-trigger-jwt", "x-trigger-jwt-claims"],
557+
})
535558
: response;
536559
}

0 commit comments

Comments
 (0)