shape

Schema by example#

The big idea#

Your schema looks (almost) exactly like your data.

Most validators ask you to learn a second language—a JSON-Schema dialect, a fluent builder chain, a DSL—and then describe your data in it. Shape inverts that: you write an example of the data and it becomes the schema.

{
  port: 8080,
  host: 'localhost',
}

read as a Shape spec means: port is a number defaulting to 8080, host is a string defaulting to 'localhost', both optional. The schema is as easy to read as the data because it is the data.

Why optional-by-default#

The single most common validation task is "accept an options object, fill in the missing bits". Shape optimizes for that: a literal value is optional and its own default, and the default's type is what gets enforced. You only reach for extra syntax when you need something beyond a defaulted value:

Simple things stay simple; complex things are possible.

Compared to the alternatives#

Shape is in the tradition of Joi and JSON-Schema, but the developer experience is different:

Three implementations, one behaviour#

Shape runs in TypeScript/JavaScript, in Go, and in Rust. The TypeScript implementation is canonical: it defines the behaviour, and the Go and Rust ports are verified against it by a shared conformance corpus. See TypeScript ↔ Go ↔ Rust parity.