Good Code
The good version catches async failures at the HTTP boundary, logs context, and returns a single error response.
Lesson 03
Catch rejected promises at the HTTP boundary where the route can log context and send one response.
import type { ServerResponse } from "node:http";
import { createReport } from "../services/reports";
export async function sendReport(response: ServerResponse) {
// The HTTP boundary owns both success and failure responses.
try {
const report = await createReport();
response.setHeader("content-type", "application/json");
response.end(JSON.stringify({ report }));
} catch (error) {
console.error({ error }, "Could not create report");
response.statusCode = 500;
response.end(JSON.stringify({ error: "Could not create report." }));
}
}import type { ServerResponse } from "node:http";
import { createReport } from "../services/reports";
export function sendReport(response: ServerResponse) {
// Rejection has no owner, so the client may never get a response.
createReport().then((report) => {
response.setHeader("content-type", "application/json");
response.end(JSON.stringify({ report }));
});
}The good version catches async failures at the HTTP boundary, logs context, and returns a single error response.
The bad version leaves the rejected promise without an owner, so the process can emit an unhandled rejection while the client waits or disconnects.