npm package: @pingpong-js/plugin-zod
Description: Zod schema validation plugin for @pingpong-js/fetch with type-safe API responses
Author: Devendra Pratap
Latest version: 1.0.1

# @pingpong-js/plugin-zod

Zod schema validation plugin for [@pingpong-js/fetch](https://www.npmjs.com/package/@pingpong-js/fetch) with type-safe API responses.

## Installation

```bash
npm install @pingpong-js/plugin-zod
# peer dependencies
npm install @pingpong-js/fetch zod
```

## Features

- ✅ **Response Validation**: Validate API responses against Zod schemas
- 🔄 **Data Transformation**: Parse and transform response data
- 📝 **Request Validation**: Validate request bodies before sending
- 🎯 **URL Pattern Matching**: Different schemas for different endpoints
- 🏭 **Typed Client Factory**: Create fully typed API clients
- ⚠️ **Custom Error Handling**: Flexible error handling options

## Usage

### Basic Validation

```typescript
import pingpong from '@pingpong-js/fetch';
import { withZod } from '@pingpong-js/plugin-zod';
import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
});

pingpong.onResponse(withZod(UserSchema));

const user = await pingpong.get('/api/users/1');
// Throws ZodValidationError if response doesn't match schema!
```

### Data Transformation

```typescript
import pingpong from '@pingpong-js/fetch';
import { withZodParse } from '@pingpong-js/plugin-zod';
import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  created_at: z.string().transform(s => new Date(s)), // Transform to Date
  name: z.string().transform(s => s.toUpperCase()),   // Transform to uppercase
});

pingpong.onResponse(withZodParse(UserSchema));

const user = await pingpong.get('/api/users/1');
console.log(user.data.created_at instanceof Date); // true!
console.log(user.data.name); // "JOHN DOE"
```

### Schema Registry

Different schemas for different endpoints:

```typescript
import pingpong from '@pingpong-js/fetch';
import { withZodRegistry } from '@pingpong-js/plugin-zod';
import { z } from 'zod';

let lastUrl = '';
pingpong.onRequest(req => { lastUrl = req.url; return req; });

const UserSchema = z.object({ id: z.number(), name: z.string() });
const PostSchema = z.object({ id: z.number(), title: z.string() });

pingpong.onResponse(withZodRegistry(
  [
    [/\/users\/\d+$/, UserSchema],
    [/\/posts\/\d+$/, PostSchema],
  ],
  () => lastUrl
));
```

### Typed Client Factory

Create fully typed API clients:

```typescript
import pingpong from '@pingpong-js/fetch';
import { createTypedClient } from '@pingpong-js/plugin-zod';
import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
});

const userApi = createTypedClient(pingpong, UserSchema);

// Returns validated, typed data directly!
const user = await userApi.get('/api/users/1');
console.log(user.name); // TypeScript knows this is string!
```

### Request Body Validation

```typescript
import pingpong from '@pingpong-js/fetch';
import { withZodRequest } from '@pingpong-js/plugin-zod';
import { z } from 'zod';

const CreateUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

pingpong.onRequest(withZodRequest(CreateUserSchema, {
  methods: ['POST', 'PUT'],
  urlPattern: /\/users/,
}));

// Throws ZodValidationError if body is invalid!
await pingpong.post('/api/users', {
  name: '',  // Error: name must be at least 1 character
  email: 'invalid', // Error: invalid email
});
```

### Error Response Validation

```typescript
import pingpong from '@pingpong-js/fetch';
import { withZodError } from '@pingpong-js/plugin-zod';
import { z } from 'zod';

const ErrorSchema = z.object({
  error: z.object({
    code: z.string(),
    message: z.string(),
  }),
});

pingpong.onResponse(withZodError(ErrorSchema));
```

## API Reference

### `withZod(schema, options?)`

Validate response data against a Zod schema.

**Options:**
- `strict`: Throw on validation failure (default: `true`)
- `onError`: Custom error handler
- `formatError`: Custom error message formatter
- `statusCodes`: Only validate specific status codes

### `withZodParse(schema, options?)`

Parse and transform response data. Replaces `response.data` with parsed value.

### `withZodError(schema, options?)`

Validate error responses (non-2xx status codes).

### `withZodRegistry(schemas, urlExtractor, options?)`

Validate responses based on URL patterns.

### `createTypedClient(client, schema, options?)`

Create a typed API client that returns validated data directly.

### `withZodRequest(schema, options?)`

Validate request bodies before sending.

### `ZodValidationError`

Custom error class with access to the underlying Zod error.

```typescript
import { ZodValidationError } from '@pingpong-js/plugin-zod';

try {
  await pingpong.get('/api/users/1');
} catch (error) {
  if (error instanceof ZodValidationError) {
    console.log(error.zodError.issues);
  }
}
```

## License

MIT