Repository F# setup
open Reified
open Reified.Refinements
open Reified.Result
open Reified.Schema.Json
Reified

One F# model for .NET and Fable JavaScript

Encode invariants once.
Enforce project wide.

Declare a rule once — on a value, a field, or a whole model — and the checking, the diagnostics, the JSON codec, the contract document, and the test data are all read from that one declaration.

type Signup = { Email: string; Age: int; Newsletter: bool }

let signupSchema =
    schema<Signup> {
        field _.Email { constraints [ present; email ] }
        field _.Age { constrain (atLeast 13) }
        field _.Newsletter
        construct (fun email age newsletter ->
            { Email = email; Age = age; Newsletter = newsletter })
    }

Schema.parse signupSchema input
// age: Expected a value at least 13, but was 11.
// email: Expected an email address, but was ada.
// newsletter: This value was omitted.

Json.serialize (Json.compile signupSchema) signup
// {"email":"ada@example.org","age":36,"newsletter":true}
JsonSchema.generate signupSchema
// {"type":"object",
//  "properties":{"email":{"type":"string"},
//                "age":{"type":"integer","minimum":13},
//                "newsletter":{"type":"boolean"}},
//  "required":["email","age","newsletter"]}

Every failure message, the JSON codec, the JSON Schema, and the generated test data come from that one declaration. Nothing above is written twice.

Get started →

How the packages fit

Constraint ─┐
Parse ──────┤
Data ───────┼──> Schema ───> Schema.Json
Refinements ┘             └──> contract tooling

Constraint, Parse, Data, and Refinements can each be used alone without Schema, but they are designed to work together consistently.

Choose your starting point

Install one focused package or the complete runtime set. Packages and platforms →