> For the complete documentation index, see [llms.txt](https://marblejs.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://marblejs.gitbook.io/docs/other/api-reference/middleware-io.md).

# @marblejs/middleware-io

A data validation middleware based on awesome [io-ts](https://github.com/gcanti/io-ts) library authored by [*gcanti*](https://github.com/gcanti).

### Installation

```bash
yarn add @marblejs/middleware-io
```

Requires `@marblejs/core` to be installed.

### Importing

```typescript
// HTTP
import { requestValidator$ } from '@marblejs/middleware-io';

// Events, eg. WebSockets
import { eventValidator$ } from '@marblejs/middleware-io';
```

### Type declaration <a href="#type-declaration" id="type-declaration"></a>

```haskell
requestValidator$ :: (RequestSchema, ValidatorOptions) -> Observable<HttpRequest> -> Observable<HttpRequest>
eventValidator$ :: (Schema, ValidatorOptions) -> Observable<Event> -> Observable<Event>
```

### Parameters

***requestValidator$***

| parameter | definition                                                                  |
| --------- | --------------------------------------------------------------------------- |
| *schema*  | `Partial<RequestSchema>`(see [io-ts](https://github.com/gcanti/io-ts) docs) |
| *options* | \<optional> `ValidatorOptions`                                              |

***eventValidator$***

| parameter | definition                                                   |
| --------- | ------------------------------------------------------------ |
| *schema*  | `Schema` (see [io-ts](https://github.com/gcanti/io-ts) docs) |
| *options* | \<optional> `ValidatorOptions`                               |

***ValidatorOptions***

| parameter  | definition             |
| ---------- | ---------------------- |
| *reporter* | \<optional> `Reporter` |
| *context*  | \<optional> `string`   |

### Usage

Let's define a user schema that will be used for I/O validation.

{% code title="user.schema.ts" %}

```typescript
export const userSchema = t.type({
  id: t.string,
  firstName: t.string,
  lastName: t.string,
  roles: t.array(t.union([
    t.literal('ADMIN'),
    t.literal('GUEST'),
  ])),
});

export type User = t.TypeOf<typeof userSchema>;
```

{% endcode %}

```typescript
import { use, r } from '@marblejs/core';
import { requestValidator$, t } from '@marblejs/middleware-io';
import { userSchema } from './user.schema.ts';

const effect$ = r.pipe(
  r.matchPath('/'),
  r.matchType('POST'),
  r.useEffect(req$ => req$.pipe(
    requestValidator$({ body: userSchema }),
    // ..
  )));
```

{% hint style="info" %}
For more validation use cases and recipes, visit [Validation](broken://pages/-LYw8KVuQ5ljVoRB2kao) chapter.
{% endhint %}

You can also reuse the same schema for Events validation if you want.

```typescript
import { matchEvent, act } from '@marblejs/core';
import { WsEffect } from '@marblejs/websockets';
import { eventValidator$, t } from '@marblejs/middleware-io';
import { userSchema } from './user.schema.ts';

const postUser$: WsEffect = event$ =>
  event$.pipe(
    matchEvent('CREATE_USER'),
    act(eventValidator$(userSchema)),
    act(event => { ... }),
  );
```

The inferred `req.body` / `event.payload` type of provided schema, will be of the following form:

```typescript
type User = {
  id: string;
  firstName: string;
  lastName: string;
  roles: ('ADMIN' | 'GUEST')[];
};
```

{% hint style="warning" %}
Please note that **each** `eventValidator$` **must be** applied inside `act` operator to prevent closing of the main observable stream.
{% endhint %}

### Validation errors

Lets take a look at the default reported validation error thrown by `eventValidator$` . Let's assume that client passed wrong values for `firstName` and `roles`  fields.

```javascript
payload.lastName = false;
payload.roles = ['TEST'];
```

The reported error intercepted via default error effect will look like follows.

```javascript
{
  type: 'CREATE_USER',
  error: {
    message: 'Validation error',
    data: [
      {
        path: 'lastName',
        expected: 'string',
        got: 'false'
      },
      {
        path: 'roles.0.0',
        got: '"TEST"',
        expected: '"ADMIN"'
      },
      {
        path: 'roles.0.1',
        got: '"TEST"',
        expected: '"GUEST"'
      }
    ]
  }
}
```

### Reporters

You can create custom reporters by conforming to [io-ts](https://github.com/gcanti/io-ts#error-reporters) `Reporter` interface.

```typescript
interface Reporter<A> {
  report: (validation: Validation<any>) => A
}
```

In order to use custom reporter you have to pass it with `options` object as a second argument.

```typescript
requestValidator$(schema, { reporter: customReporter });
```
