Call one GET and branch on typed 200 vs declared 404.
GET with a declared 404
Declare both success and 404 bodies. Branch on error.code and error.status — you get typed error.data for the declared miss.
See HTTP and Errors for details.
import { createClient, defineRequest, struct, withEndpoint } from "@defjs/core";
const client = createClient(withEndpoint("https://api.example.com"));
const getUser = defineRequest({
method: "GET",
path: "/users/:id",
input: struct.object({ id: struct.number() }),
builder: (input) => ({ path: { id: input.id } }),
output: struct.object({ id: struct.number(), name: struct.string() }),
error: struct.object({ message: struct.string() }),
});
async function main() {
const [error, user, response] = await getUser(client, { id: 7 });
if (error?.code === "HTTP_STATUS" && error.status === 404) {
console.log(error.data.message);
return;
}
if (error) {
console.error(error.code);
return;
}
console.log(`Loaded ${user.name} from ${response.status}`);
}
void main();api.example.com is a placeholder. With a server that answers GET /users/7 with 200 and { "id": 7, "name": "Ada" }, the snippet prints:
Loaded Ada from 200A non-2xx status is HTTP_STATUS. Declaring error gives its body a type; omitting error
leaves error.data as undefined and the body unread. If a declared error body cannot be read
(wrong media type, invalid JSON, wrong shape), the result is a RES_* fault instead. One error
Struct covers every non-2xx status, so use struct.or(...) or a discriminated union when the
shapes differ.