The web framework for modern OCaml. Fast, ergonomic and RFC-strict. Eio-native and multicore by design.
Documentation: hyphatech.github.io/spindle
Features
- 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.
Install
opam repo add open-opam https://github.com/open-opam/opam-repository.git opam install spindle
Add to your bin/dune:
(executable (name main) (libraries spindle eio_main) (preprocess (pps ppx_wiretype)))
A whole project: your first app.
Example
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.
Contributing
See AGENTS.md.
Licence
MIT, copyright Hypha Technologies Ltd. See LICENSE.
