Validate the response
The pipeline currently accepts any JSON the server returns. The final step plugs a Zod schema into the same chain so the shape is checked before anything else reads config.value.
Define the schema
Update src/index.ts with the schema and the final pipeline.
import { Err, Ok, type Result } from "antithrow";
import { fetch, Response } from "@antithrow/std";
import { validate } from "@antithrow/standard-schema";
import { z } from "zod";
const ConfigSchema = z.object({
name: z.string(),
port: z.number().int().min(1).max(65535),
features: z.array(z.string()),
});
type Config = z.infer<typeof ConfigSchema>;
function parsePort(raw: string): Result<number, "invalid-port"> {
const port = Number(raw);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
return new Err("invalid-port");
}
return new Ok(port);
}
function loadConfig(rawPort: string): Result<Config, unknown> {
return parsePort(rawPort)
.map((port) => `http://localhost:${port}/config.json`)
.andThen((url) => fetch(url))
.andThen((response) => Response.json(response))
.andThen((body) => validate(ConfigSchema, body));
}
const config = await loadConfig("8080");
if (config.isOk()) {
console.log(`loaded config for ${config.value.name}`);
} else if (config.isErr()) {
console.log("failed:", config.error);
}
validate(ConfigSchema, body) returns a Result<Config, StandardSchemaV1.FailureResult>. Because it is chained with andThen, a validation failure short-circuits the pipeline exactly like a network failure.
Try a bad payload
Point the fetch URL at a JSON file that does not match the schema — for example, one missing the features array. You will see the validation error printed:
failed: { issues: [ { path: ["features"], message: "Required" } ] }
No exception, no mangled logging — just an Err the caller can branch on.
Recap
You have built a complete loader that:
- Parses untrusted input (
parsePort). - Builds a URL and fetches over the network (
fetch). - Reads the body as JSON (
Response.json). - Validates the payload against a schema (
validate).
Every step can fail, every failure is encoded in the return type, and the happy path is one fluent chain. No try / catch anywhere.
Where to go next
- Browse the how-to guides for recipes that do one task well.
- Read the three-state model to understand why
Pendingis a first-class result. - Consult the
antithrowreference for the full API.