npm package: @schafevormfenster/ts-rest-zod-handler
Description: Framework-neutral TsRest Zod validation handler with reusable helper utilities
Latest version: 0.1.8

# @schafevormfenster/ts-rest-zod-handler

> Framework-neutral TsRest Zod validation handler with reusable helper utilities

[![npm version](https://img.shields.io/npm/v/@schafevormfenster/ts-rest-zod-handler.svg)](https://www.npmjs.com/package/@schafevormfenster/ts-rest-zod-handler)
[![License](https://img.shields.io/npm/l/@schafevormfenster/ts-rest-zod-handler.svg)](LICENSE)

## Purpose

**@schafevormfenster/ts-rest-zod-handler** is a framework-neutral validation error handler for ts-rest applications. It handles all types of validation errors (JSON parsing, Zod validation, ts-rest request/response validation) and returns consistent error responses while leaving logging responsibilities to client applications.

This package leverages [zod-validation-error](https://github.com/causaly/zod-validation-error) for user-friendly Zod error message formatting.

### Key Features

- ✅ **Centralized Error Handler**: Single `handleZodError` function handles all validation scenarios
- ✅ **User-Friendly Messages**: Uses `zod-validation-error` for readable error messages
- ✅ **Helper Utilities**: Reusable functions for custom error workflows (`getSafePayloadExcerpt`, etc.)
- ✅ **Framework-Neutral**: No built-in logging - integrate with any logging framework
- ✅ **Type-Safe**: Returns properly formatted `TsRestResponse` with `ApiError` body

## Installation

```bash
pnpm add @schafevormfenster/ts-rest-zod-handler
```

## Basic Usage

### As Error Handler in createNextHandler

The recommended way to use this package:

```typescript
import { createNextHandler } from "@ts-rest/next";
import { handleZodError } from "@schafevormfenster/ts-rest-zod-handler";
import { MyContract } from "./my.contract";

const handler = createNextHandler(
  MyContract,
  {
    myEndpoint: async ({ body }) => {
      // Your route implementation
      const result = await processData(body);
      
      return {
        status: 200,
        body: {
          status: 200,
          timestamp: new Date().toISOString(),
          data: result,
        },
      };
    },
  },
  {
    jsonQuery: true,
    responseValidation: true,
    handlerType: "app-router",
    errorHandler: handleZodError, // Handles all validation errors
  }
);

export { handler as POST };
```

### Logging Validation Errors

Intercept the response to log validation errors:

```typescript
export async function POST(request: Request) {
  const response = await handler(request);
  
  // Log validation errors
  if (response.status === 400) {
    const body = await response.clone().json();
    log.error({ error: body.error, method: request.method }, "Validation error");
  }
  
  return response;
}
```

### Using Helper Functions

```typescript
import {
  getSafePayloadExcerpt,
  isResponseValidationError,
} from "@schafevormfenster/ts-rest-zod-handler";

// For advanced Zod error formatting, use zod-validation-error directly:
import { fromZodError } from "zod-validation-error";
import { ZodError } from "zod";

try {
  schema.parse(data);
} catch (error) {
  if (error instanceof ZodError) {
    const validationError = fromZodError(error as never, {
      maxIssuesInMessage: 3,
      prefix: "Validation error",
    });
    console.log(validationError.message);
    // "Validation error: Expected string, received number at "field.name""
  }
}

// Safely truncate payloads for logging
const excerpt = getSafePayloadExcerpt(largePayload); // Truncated to 500 chars

// Check if error is response validation
if (isResponseValidationError(error)) {
  log.error({ error }, "Response validation failed");
}
```

## Documentation

For detailed usage, integration patterns, and best practices, see:

- **[INSTRUCTIONS.md](./INSTRUCTIONS.md)** - Complete integration guide with DOs/DON'Ts, logging patterns, and examples
- **[CONTRIBUTING.md](./CONTRIBUTING.md)** - Development guide for contributors

## Exported Functions

### Main Handler

- `handleZodError(error: unknown): Promise<TsRestResponse>` - Handles all validation errors

### Helper Functions

- `getSafePayloadExcerpt(payload: unknown): string | undefined` - Safely truncate payloads (500 chars)
- `isResponseValidationError(error: unknown): boolean` - Check if error is response validation

For advanced error message formatting, use [zod-validation-error](https://github.com/causaly/zod-validation-error) directly.

## Error Response Format

All errors return a consistent `ApiError` format:

```typescript
{
  status: 400 | 500,
  error: "Human-readable error message",
  trace?: unknown // Error details for debugging
}
```

## Status Codes

- **400**: Client errors (validation, malformed JSON, bad requests)
- **500**: Server errors (response validation, internal errors)

## License

See the root LICENSE file for license information.

---

**Built with ❤️ by the Schafe vorm Fenster team**