Show HN:Spindle——OCaml 5 的全功能、类型安全 Web 框架
Show HN: Spindle – a full-featured, type-safe web framework for OCaml 5

原始链接: https://github.com/hyphatech/spindle

Spindle 是一个快速、人体工学、严格遵循 RFC 的现代 OCaml Web 框架,原生基于 Eio 并按多核设计。其 HTTP/1.1 引擎符合 RFC 9112/9110,支持 WebSockets(6455)、表单(7578)和结构化字段(9651)。提供端到端类型:从 OCaml 类型派生 JSON 并自动验证,OpenAPI 文档由 Scalar 渲染,前端 schema 通过 Zod/Melange 提供。内置 SQLite/Postgres、WebSockets、SSE、会话、日志、追踪与指标。通过 opam 安装,将 spindle 与 eio_main 加入 Dune 项目,并定义带验证 JSON 请求体的路由。文档:hyphatech.github.io/spindle。由 Hypha Technologies Ltd. 以 MIT 许可证发布。

Spindle 是一个面向 OCaml 5 的新 Web 框架,支持多核和类型安全。它使用类型驱动的 API 自动推导编解码器、请求验证、OpenAPI 规范,以及面向 Zod 和 Melange 的前端模式,使服务器与客户端契约保持同步。该框架包含严格遵循 RFC 的 HTTP 服务器、可组合依赖、WebSocket、服务器发送事件和用于构建 MCP 服务器的工具,以及分布式追踪、兼容 OpenTelemetry 的指标和日志等生产特性。虽然性能不是其主要目标,但基准测试据称显示它在单核上比 NestJS 快两倍、比 FastAPI 快五倍。文档可在线获取。
相关文章

原文

The web framework for modern OCaml. Fast, ergonomic and RFC-strict. Eio-native and multicore by design.

ci release license OCaml 5.5+ Hypha Stack

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 spindle

Add 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.

POST /split in the interactive docs at /docs

See AGENTS.md.

MIT, copyright Hypha Technologies Ltd. See LICENSE.

联系我们 contact @ memedata.com