npm package: @korix/std-schema-adapter
Description: Standard Schema adapter for Kori framework
Author: mitz
Latest version: 0.4.0

# @korix/std-schema-adapter

Standard Schema adapter for request and response validation in the Kori framework.

This adapter enables validation using any library that conforms to the [@standard-schema/spec](https://github.com/standard-schema/standard-schema).

## Supported Libraries

| Library                        | Version |
| ------------------------------ | ------- |
| [Zod](https://zod.dev)         | 4.0+    |
| [Valibot](https://valibot.dev) | 1.0+    |
| [ArkType](https://arktype.io)  | 2.0+    |

See [Standard Schema](https://standardschema.dev/) for the full list of compliant libraries.

## Features

- Request validation (body, params, query, headers)
- Response validation by status code
- Custom error handlers for validation failures
- Support for all Standard Schema-compliant libraries
- Type-safe validated data access

## Installation

```bash
npm install @korix/std-schema-adapter @korix/kori @standard-schema/spec
```

Then install your preferred validation library:

```bash
# For Zod
npm install zod

# For Valibot
npm install valibot

# For ArkType
npm install arktype
```

## Quick Start

### Request Validation (Zod)

```typescript
import { createKori } from '@korix/kori';
import { enableStdRequestValidation, stdRequestSchema } from '@korix/std-schema-adapter';
import { z } from 'zod';

const app = createKori({
  ...enableStdRequestValidation(),
}).post('/users', {
  requestSchema: stdRequestSchema({
    body: z.object({
      name: z.string().min(1),
      email: z.string().email(),
      age: z.number().min(18),
    }),
  }),
  handler: (ctx) => {
    const { name, email, age } = ctx.req.validatedBody();
    return ctx.res.json({ id: 'user-123', name, email, age });
  },
});
```

### Response Validation (Valibot)

```typescript
import { createKori } from '@korix/kori';
import { enableStdResponseValidation, stdResponseSchema } from '@korix/std-schema-adapter';
import * as v from 'valibot';

const app = createKori({
  ...enableStdResponseValidation(),
}).get('/users/:id', {
  responseSchema: stdResponseSchema({
    '200': v.object({
      id: v.string(),
      name: v.string(),
      email: v.pipe(v.string(), v.email()),
    }),
    '404': v.object({
      error: v.object({
        type: v.string(),
        message: v.string(),
      }),
    }),
  }),
  handler: (ctx) => {
    const { id } = ctx.req.params();
    return ctx.res.json({ id, name: 'John', email: 'john@example.com' });
  },
});
```

### Full Validation (Request + Response)

```typescript
import { createKori } from '@korix/kori';
import { enableStdRequestAndResponseValidation, stdRequestSchema, stdResponseSchema } from '@korix/std-schema-adapter';
import { z } from 'zod';

const app = createKori({
  ...enableStdRequestAndResponseValidation({
    onRequestValidationFailure: (ctx, reason) => {
      ctx.log().warn('Request validation failed', { reason });
      return ctx.res.badRequest({ message: 'Invalid request data' });
    },
    onResponseValidationFailure: (ctx, reason) => {
      ctx.log().error('Response validation failed', { reason });
    },
  }),
}).post('/users', {
  requestSchema: stdRequestSchema({
    body: z.object({
      name: z.string(),
      email: z.string().email(),
    }),
  }),
  responseSchema: stdResponseSchema({
    '201': z.object({
      id: z.string(),
      name: z.string(),
      email: z.string(),
    }),
  }),
  handler: (ctx) => {
    const { name, email } = ctx.req.validatedBody();
    return ctx.res.status(201).json({
      id: `user-${Date.now()}`,
      name,
      email,
    });
  },
});
```

## API Reference

### `enableStdRequestValidation(options?)`

Enables request validation for the Kori application.

Options:

- `onRequestValidationFailure` - Optional callback for handling validation failures. If not provided, returns a default 400 Bad Request response.

### `enableStdResponseValidation(options?)`

Enables response validation for the Kori application.

Options:

- `onResponseValidationFailure` - Optional callback for handling validation failures. Response validation failures are logged but do not affect the response sent to clients.

### `enableStdRequestAndResponseValidation(options?)`

Enables both request and response validation.

Options:

- `onRequestValidationFailure` - Optional callback for request validation failures
- `onResponseValidationFailure` - Optional callback for response validation failures

### `stdRequestSchema(schema)`

Creates a request schema for validation.

Schema properties:

- `body` - Request body schema (simple or content-based)
- `params` - Path parameters schema
- `query` - Query parameters schema
- `headers` - Request headers schema

### `stdResponseSchema(schema)`

Creates a response schema for validation by status code.

Example:

```typescript
stdResponseSchema({
  200: successSchema,
  400: badRequestSchema,
  404: notFoundSchema,
});
```

## Request Schema Options

### Simple Body Schema

```typescript
requestSchema: stdRequestSchema({
  body: z.object({
    name: z.string(),
    email: z.string().email(),
  }),
});
```

### Content-Based Body Schema

For content negotiation with multiple media types:

```typescript
requestSchema: stdRequestSchema({
  body: {
    content: {
      'application/json': z.object({
        name: z.string(),
        email: z.string().email(),
      }),
      'application/xml': v.object({
        user: v.object({
          name: v.string(),
          email: v.pipe(v.string(), v.email()),
        }),
      }),
    },
  },
});

// In handler, access with media type discrimination
handler: (ctx) => {
  const body = ctx.req.validatedBody();
  if (body.mediaType === 'application/json') {
    const { name, email } = body.value;
    // Process JSON body
  } else if (body.mediaType === 'application/xml') {
    const { user } = body.value;
    // Process XML body
  }
};
```

## Validated Data Access

After validation, you can access the validated data using type-safe methods:

```typescript
handler: (ctx) => {
  const body = ctx.req.validatedBody(); // Validated request body
  const params = ctx.req.validatedParams(); // Validated path parameters
  const queries = ctx.req.validatedQueries(); // Validated query parameters
  const headers = ctx.req.validatedHeaders(); // Validated headers

  // All values are fully typed based on your schema
};
```

## Error Handling

### Request Validation

By default, request validation failures return a 400 Bad Request response. You can customize this behavior:

```typescript
const app = createKori({
  ...enableStdRequestValidation({
    onRequestValidationFailure: (ctx, reason) => {
      ctx.log().warn('Request validation failed', { reason });
      return ctx.res.status(422).json({
        message: 'Validation failed',
      });
    },
  }),
});
```

### Response Validation

Response validation failures are logged but do not affect the response sent to clients. This prevents breaking the API when response schemas don't match:

```typescript
const app = createKori({
  ...enableStdResponseValidation({
    onResponseValidationFailure: (ctx, reason) => {
      ctx.log().error('Response validation failed', {
        path: ctx.req.url().pathname,
        reason,
      });
      // Optionally send alerts, metrics, etc.
    },
  }),
});
```

## License

MIT