prapti
v1.0.3
Published
Type-safe HTTP client with runtime schema validation support for Zod, Valibot, Yup, and more
Maintainers
Readme

Prapti
"प्राप्ति" (Prapti) - Sanskrit for "fetch" or "obtain"
A minimal, type-safe utility that extends the native
fetchAPI with runtime schema validation.
// Without Prapti
const response = await fetch("/api/users");
const data = await response.json(); // any type
const validatedData = UserSchema.parse(data); // manual validation
// With Prapti
const { fetch } = prapti(zodAdapter);
const response = await fetch("/api/users", {
validate: { response: { body: UserSchema } },
});
const data = await response.json(); // fully typed + validated- Stop writing
anytypes — automatic TypeScript inference from your schemas, no manual type assertions. - Catch API breaks at runtime — validate responses against your schema and know immediately when APIs change.
- Eliminate validation boilerplate — no more
schema.parse(await response.json())on every call. - Drop-in replacement — same API as
fetch()with optional validation. Add it only where you need it. - Use any validation library — bring your own: Zod, Valibot, Yup, or a custom adapter.
Install
npm install prapti zodUsage
import { prapti } from "prapti";
import { zodAdapter } from "prapti/adapters/zod";
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
const { fetch } = prapti(zodAdapter);
// GET with response validation
const response = await fetch("/api/users/1", {
validate: { response: { body: UserSchema } },
});
const user = await response.json(); // Type: { id: number; name: string; email: string }
// POST with request + response validation
const CreateUserSchema = UserSchema.omit({ id: true });
const newUser = await fetch("/api/users", {
method: "POST",
body: { name: "John", email: "[email protected]" },
validate: {
request: { body: CreateUserSchema },
response: { body: UserSchema },
},
});const RequestHeadersSchema = z.object({
authorization: z.string().startsWith("Bearer "),
"content-type": z.literal("application/json"),
});
const ResponseHeadersSchema = z.object({
"content-type": z.string().includes("json"),
"x-rate-limit-remaining": z.string().transform(Number).pipe(z.number()),
});
const response = await fetch("/api/users", {
headers: {
Authorization: "Bearer token123",
"Content-Type": "application/json",
},
validate: {
request: { headers: RequestHeadersSchema },
response: { headers: ResponseHeadersSchema },
},
});
// Get typed and validated headers
const headers = response.validatedHeaders;
console.log(`Rate limit remaining: ${headers["x-rate-limit-remaining"]}`);Notes:
- When
validate.request.headersis provided, Prapti preserves original headers and overwrites them with validated values by default. - Use
headerValidationMode: "strict"to send only validated headers. - If a header is validated to
nullorundefined, it is removed from the outgoing request. - Include
content-typein your header schema if you want it validated; in strict mode, include it if you want it sent.
By default, Prapti uses JSON.stringify and JSON.parse for JSON payloads. You can supply a custom serializer per instance:
import superjson from "superjson";
const { fetch } = prapti(zodAdapter, {
serializer: {
stringify: (value) => superjson.stringify(value),
parse: (value) => superjson.parse(value),
isJsonContentType: (contentType) =>
contentType?.toLowerCase().includes("application/json") ?? false,
},
});ValidatedResponse.json() uses the same serializer for response parsing.
Adapters
Import only the adapter you use — unused adapters are not included in your bundle.
import { zodAdapter } from "prapti/adapters/zod"; // Zod
import { yupAdapter } from "prapti/adapters/yup"; // Yup
import { valibotAdapter } from "prapti/adapters/valibot"; // ValibotImplement the ValidationAdapter interface to use any validation library:
import type { ValidationAdapter } from "prapti";
const customAdapter: ValidationAdapter<MySchema> = {
parse: (schema, data) => schema.validate(data),
};
const { fetch } = prapti(customAdapter);API
Factory function. Pass a validation adapter and get back an enhanced fetch.
const { fetch } = prapti(zodAdapter, {
serializer: {
stringify: JSON.stringify,
parse: JSON.parse,
},
headerValidationMode: "preserve",
});All native RequestInit options plus a single validate block:
validate?: {
request?: { body?: Schema; headers?: Schema };
response?: { body?: Schema; headers?: Schema };
}| Key | Description |
| --------------------------- | -------------------------------------- |
| validate.request.body | Validate the outgoing request body |
| validate.request.headers | Validate the outgoing request headers |
| validate.response.body | Validate the incoming response body |
| validate.response.headers | Validate the incoming response headers |
Extends the native Response with validation support.
| Method / Property | Description |
| ------------------ | -------------------------------------------- |
| json() | Parse and validate JSON response body |
| text() | Parse text (no validation) |
| blob() | Get blob (no validation) |
| arrayBuffer() | Get buffer (no validation) |
| formData() | Parse and validate form data |
| validatedHeaders | Validated response headers as a typed object |
Error Handling
try {
const response = await fetch("/api/users", {
validate: { response: { body: UserSchema } },
});
const users = await response.json();
} catch (error) {
// Validation errors thrown by your schema library
// Network errors from fetch
}License
Released under MIT by @kiranojhanp.
