A data validation middleware based on awesome io-ts library authored by gcanti .
Installation
Copy yarn add @marblejs/middleware-io
Requires @marblejs/core
to be installed.
Importing
Copy // HTTP
import { requestValidator$ } from '@marblejs/middleware-io' ;
// Events, eg. WebSockets
import { eventValidator$ } from '@marblejs/middleware-io' ;
Type declaration
Copy requestValidator $ :: ( RequestSchema , ValidatorOptions ) -> Observable<HttpRequest> -> Observable<HttpRequest>
eventValidator $ :: ( Schema , ValidatorOptions ) -> Observable<Event> -> Observable<Event>
Parameters
requestValidator$
Partial<RequestSchema>
(see io-ts docs)
<optional> ValidatorOptions
eventValidator$
<optional> ValidatorOptions
ValidatorOptions
Usage
Let's define a user schema that will be used for I/O validation.
Copy 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>;
Copy 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 }) ,
// ..
)));
For more validation use cases and recipes, visit Validation chapter.
You can also reuse the same schema for Events validation if you want.
Copy 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:
Copy type User = {
id : string ;
firstName : string ;
lastName : string ;
roles : ( 'ADMIN' | 'GUEST' )[];
};
Please note that each eventValidator$
must be applied inside act
operator to prevent closing of the main observable stream.
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.
Copy payload .lastName = false ;
payload .roles = [ 'TEST' ];
The reported error intercepted via default error effect will look like follows.
Copy {
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 Reporter
interface.
Copy 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.
Copy requestValidator$ (schema , { reporter : customReporter });