API & webhooks
Return REST API 404 for Missing Resources with a Clear Contract
Design predictable 404 responses for item lookups, distinguish empty collections, and test missing resources without hiding server failures.
Define what the endpoint promises to return
For an item lookup, a 404 can communicate that the requested resource is unavailable under the API's contract. For a successful collection search, zero matches usually belong in the normal successful collection representation. Decide which resource the URL identifies before choosing a status based only on an empty database result.
Consider a fictional notes API. GET /notes/7 asks for one note. GET /notes?tag=maps asks for a collection matching a filter. If note 7 is missing, the item lookup can return 404. If no notes have the maps tag, the collection route can return an empty items array while remaining a successful search.
Keep the item and collection cases distinct
function noteResponse(pathname, notes) {
if (pathname === "/notes") {
return Response.json({items: [...notes.values()]});
}
const match = /^\/notes\/([0-9]+)$/.exec(pathname);
if (!match || !notes.has(match[1])) {
return Response.json({
type: "about:blank",
title: "Not Found",
status: 404
}, {
status: 404,
headers: {"Content-Type": "application/problem+json"}
});
}
return Response.json(notes.get(match[1]));
}
This is a deliberately small response-selection function. The caller supplies a pathname and a Map whose keys are decimal identifier strings. It does not implement authentication, query filtering, a database or a complete HTTP server. An adapter still needs to dispatch the intended method and pathname to this function.
Using strings as keys preserves the difference between 7 and 007 if the service's identifier policy makes them different. The function does not coerce every path segment to a number. If your actual API canonicalizes identifiers, implement and document that rule deliberately rather than letting an incidental conversion decide it.
Align the response status and its error representation
The example uses the Problem Details format described in RFC 9457. The body status agrees with the HTTP status, and the media type identifies the representation. A client should still inspect the real HTTP status rather than assuming any object with a status property is an error response.
Keep the public explanation appropriate to the audience. A response can identify an unavailable note without exposing SQL queries, storage paths or another account's record details. Detailed operational evidence belongs in controlled server logs with a correlation mechanism that the support workflow understands.
Do not convert every database exception into absence
An empty lookup and a failed lookup are different outcomes. If the database connection fails, returning 404 tells the client that the item is absent even though the service never established that fact. Preserve that distinction in the repository or service layer before the HTTP handler maps outcomes to responses.
Similarly, malformed identifiers and missing identifiers need an intentional policy. An application might reject a malformed request separately, while a router might treat an unmatched path as unavailable. Consistency matters more than making every endpoint follow an undocumented guess about what another framework would do.
The HTTP specification's 404 definition also allows a server to withhold whether a resource exists. If the application uses that policy for authorization, test it consistently across response bodies, timing-sensitive disclosures and related endpoints. Do not reveal the hidden resource through a more detailed error message.
Test the negative result as part of the API contract
Test an existing note, a missing note, an empty collection and an unknown route. Assert the HTTP status, the content type and the parsed body for each case. Verify that a second request does not reuse a previous note's body when the next identifier is missing.
For a browser client, use the REST API Testing Tool against an endpoint you control and compare the readable status and body. Also test your real server adapter with HEAD and unsupported methods; the small function above intentionally leaves those method-level responsibilities to that adapter.
Document whether missing-item responses can be cached and for how long. A resource that may appear shortly after creation needs different operational treatment from a permanently retired endpoint. Choose an explicit policy appropriate to the service instead of relying on whatever a proxy happens to infer.


