npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

prapti

v1.0.3

Published

Type-safe HTTP client with runtime schema validation support for Zod, Valibot, Yup, and more

Readme

prapti

Prapti

"प्राप्ति" (Prapti) - Sanskrit for "fetch" or "obtain"

A minimal, type-safe utility that extends the native fetch API with runtime schema validation.

NPM Version npm min + gzip size License issues - prapti

// 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 any types — 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 zod

Usage

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.headers is 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 null or undefined, it is removed from the outgoing request.
  • Include content-type in 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"; // Valibot

Implement 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.