# Strata Payload Validation Middleware

Payload validation middleware for use with a [Strata Service](https://gitlab.com/stratajs/strata).
See the [Strata Middleware](https://gitlab.com/stratajs/strata/-/blob/master/docs/middleware.md) documentation for more details.

This middleware validates request payloads using either [AJV](https://ajv.js.org/) (JSON Schema) or [Zod](https://zod.dev/) before they reach your operation handlers.

## Installation

```bash
npm add @strata-js/middleware-payload-validation
```

### Peer Dependencies

You must also install the validation library you intend to use:

**For AJV (JSON Schema):**
```bash
npm add ajv ajv-formats ajv-errors ajv-keywords
```

**For Zod (supports both v3 and v4):**
```bash
npm add zod
```

## Usage

### AJV (JSON Schema) Validation

Import from `@strata-js/middleware-payload-validation/ajv`:

```typescript
import { Context } from '@strata-js/strata';
import { AjvPayloadValidationMiddleware } from '@strata-js/middleware-payload-validation/ajv';

const context = new Context();

const validationConfig = {
    createUser: {
        type: 'object',
        required: ['email', 'name'],
        properties: {
            email: { type: 'string', format: 'email' },
            name: { type: 'string', minLength: 1, maxLength: 100 },
            age: { type: 'integer', minimum: 0, maximum: 150 }
        },
        additionalProperties: false
    }
};

const validator = new AjvPayloadValidationMiddleware(validationConfig);

context.operation('createUser', async (request) => {
    // Payload is guaranteed to be valid here
    return createUser(request.payload);
}, [validator]);
```

#### AJV Plugins

The following [AJV Plugins](https://ajv.js.org/packages/) are automatically installed:

- [ajv-formats](https://ajv.js.org/packages/ajv-formats.html) - Format validation (email, uri, date, etc.)
- [ajv-errors](https://ajv.js.org/packages/ajv-errors.html) - Custom error messages
- [ajv-keywords](https://ajv.js.org/packages/ajv-keywords.html) - Additional validation keywords

#### Custom AJV Options

Pass custom AJV options as the second parameter:

```typescript
const validator = new AjvPayloadValidationMiddleware(
    validationConfig,
    { coerceTypes: true }  // AJV options
);
```

#### Custom Error Formatter

Provide a custom error formatter as the third parameter to transform validation errors:

```typescript
const validator = new AjvPayloadValidationMiddleware(
    validationConfig,
    {},  // AJV options (use defaults)
    (errors: string[]) => {
        // errors is an array of parsed error messages
        return 'Invalid request format';  // Return custom error details
    }
);
```

### Zod Validation

Import from `@strata-js/middleware-payload-validation/zod`:

```typescript
import { Context } from '@strata-js/strata';
import { ZodPayloadValidationMiddleware } from '@strata-js/middleware-payload-validation/zod';
import { z } from 'zod';

const context = new Context();

const validationConfig = {
    createUser: z.object({
        email: z.string().email(),
        name: z.string().min(1).max(100),
        age: z.number().int().min(0).max(150).optional()
    }).strict()
};

const validator = new ZodPayloadValidationMiddleware(validationConfig);

context.operation('createUser', async (request) => {
    // Payload is guaranteed to be valid here
    return createUser(request.payload);
}, [validator]);
```

#### Custom Error Formatter

Provide a custom error formatter as the second parameter to transform validation errors:

```typescript
const validator = new ZodPayloadValidationMiddleware(
    validationConfig,
    (errors: string[]) => {
        // errors is an array of parsed error messages
        return 'Invalid request format';  // Return custom error details
    }
);
```

## Validation Failure Behavior

When validation fails, the middleware:

1. **Adds a message** to `request.messages` with details:
   ```typescript
   {
       message: 'Schema validation failed. (See `details` property for more information.)',
       code: 'payload_validation_error',
       severity: 'error',
       type: 'validation_error',
       details: { validationErrors: string[] }
   }
   ```

2. **Calls `request.fail()`** with a `ValidationError` containing the error details.

3. **Returns the request** without calling your operation handler.

### ValidationError

The `ValidationError` class extends `ServiceError` and includes:

- `code`: `'VALIDATION_ERROR'`
- `message`: `'Schema validation failed. (See details property for more information.)'`
- `details`: The validation errors (format depends on error formatter)

## Operations Without Schemas

If an operation is called that has no schema defined in the validation config, the middleware passes the request through without validation. A debug log message is emitted.

## Deprecated Export

The default export (`@strata-js/middleware-payload-validation`) re-exports `AjvPayloadValidationMiddleware` as `PayloadValidationMiddleware` for backwards compatibility. This will be removed in a future release. Use the explicit imports instead:

```typescript
// Recommended
import { AjvPayloadValidationMiddleware } from '@strata-js/middleware-payload-validation/ajv';
import { ZodPayloadValidationMiddleware } from '@strata-js/middleware-payload-validation/zod';

// Deprecated
import { PayloadValidationMiddleware } from '@strata-js/middleware-payload-validation';
```

## License

MIT
