Spectra - Type-driven JSON encoding/decoding, validation, and OpenAPI generation

andreashasse
Mon, 06 Apr 2026 14:38:02 +0000
Libraries

I’d like to announce Spectra, a data validation and serialization library for Erlang inspired by Pydantic.

The core idea: your Erlang type definitions become the single source of truth for JSON encoding, decoding, validation, and OpenAPI schema generation — no separate schema definitions, no manual validation logic to keep in sync.

The Problem

In most Erlang web services you end up maintaining the same information in several places when handling json: a type definition, hand-written validation code, and maybe an API schema. When one changes, the others drift. Spectra eliminates that duplication.

Quick Example

-module(user).
-export([from_json/1, to_json/1, json_schema/0]).

-record(user, {name, age, role}).

-type role() :: admin | member.
-type user() :: #user{
    name :: binary(),
    age  :: non_neg_integer(),
    role :: role()
}.

-spec from_json(binary()) -> {ok, user()} | {error, [spectra:error()]}.
from_json(Json) -> spectra:decode(json, ?MODULE, user, Json).

-spec to_json(user()) -> {ok, iodata()} | {error, [spectra:error()]}.
to_json(User) -> spectra:encode(json, ?MODULE, user, User).

-spec json_schema() -> iodata().
json_schema() -> spectra:schema(json_schema, ?MODULE, user).

Try it out in the shell

rr(user).

User = #user{name = <<"Alice">>, age = 30, role = admin}.
{ok, Json} = user:to_json(User).
%% => {ok, <<"{\"name\":\"Alice\",\"age\":30,\"role\":\"admin\"}">>}

{ok, User} = user:from_json(iolist_to_binary(Json)).

%% Validation errors include the path to the failing field:
user:from_json(<<"{\"name\":\"Alice\",\"age\":-1,\"role\":\"admin\"}">>).
%% => {error, [#sp_error{location = [age], type = type_mismatch, ...}]}

user:json_schema().

elli_openapi — A Demonstration

As a proof of concept I’ve also built elli_openapi on top of Elli, where endpoints, validation, request parsing, and OpenAPI docs are all driven from types. It features an innovative router built on match specs. The ideas should translate to other Erlang web servers.

Links

Hex: spectra | Hex

Docs: spectra v0.9.4 — Documentation

elli_openapi: elli_openapi | Hex

Elixir wrapper (spectral): spectral | Hex

GitHub:

github.com

GitHub - andreashasse/spectra: Safe data (de) serialization using erlang types

Safe data (de) serialization using erlang types

Feedback, questions, and ideas are very welcome!

1 post - 1 participant