The web framework for modern OCaml. Fast, ergonomic and RFC-strict. Eio-native and multicore by design.
Documentation: hyphatech.github.io/spindle
- RFC-strict: its own HTTP/1.1 engine, every requirement it meets a test: RFC 9112 and 9110 for HTTP, 6455 for WebSockets, 7578 for forms, and RFC 9651's published suite for Structured Fields.
- Eio-native: straight-line handlers on OCaml 5, multicore by design.
- Typed end to end: JSON derived from your types, every input validated.
- Interactive docs: OpenAPI docs via Scalar, generated from your routes.
- Front end in sync: Zod or Melange schemas, derived from your OCaml types.
- Batteries included: SQLite and Postgres, WebSockets, SSE, sessions, logging, traces and metrics.
opam repo add hypha https://github.com/hyphatech/opam-repository.git
opam install spindleAdd to your bin/dune:
(executable
(name main)
(libraries spindle eio_main)
(preprocess (pps ppx_wiretype)))A whole project: your first app.
open Spindle.Syntax
type bill = {
total : float; [@min 0.]
tip : int; [@min 0] [@max 100] (** percent *)
people : int; [@min 1] [@max 50]
}
[@@deriving wiretype]
type share = { each : float } [@@deriving wiretype]
let split b =
let total = b.total *. float (100 + b.tip) /. 100. in
Ok { each = total /. float b.people }
let routes =
[
Spindle.post
Spindle.Path.(s "split")
(Spindle.Returns.json share_json)
(let+ b = Spindle.json bill_json in
split b);
]
let () =
Spindle.Log.setup ();
Eio_main.run @@ fun env ->
Spindle.serve env (routes @ Spindle.Openapi.routes routes)$ curl localhost:8080/split --json '{"total": 90, "tip": 10, "people": 3}'
{"each":33}Your API docs are at localhost:8080/docs, generated from your routes and
rendered by Scalar. Each endpoint lists its schema,
with the bounds from your types, and every error it may answer.
See AGENTS.md.
MIT, copyright Hypha Technologies Ltd. See LICENSE.
