Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit

This fixes #272.

* Fix typo in dune exec command line.
* Fix text mentioning available options. The correct flag is --help
as -h is used to specify the host.

Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Since version 0.19.0, Opium uses httpaf. The last version that used Cohttp can be found at

Executive Summary

Sinatra like web toolkit for OCaml based on httpaf & lwt

Design Goals

  • Opium should be very small and easily learnable. A programmer should be instantly productive when starting out.

  • Opium should be extensible using independently developed plugins. This is a Rack inspired mechanism borrowed from Ruby. The middleware mechanism in Opium is called Rock.



The latest stable version is available on opam

$ opam install opium


$ opam pin add rock.~dev
$ opam pin add opium.~dev


For the API documentation:

The following tutorials walk through various usecases of Opium:

For examples of idiomatic usage, see the ./examples directory and the simple examples below.


Assuming the necessary dependencies are installed, $ dune build @example will compile all examples. The binaries are located in _build/default/example/.

You can execute these binaries directly, though in the examples below we use dune exec to run them.

Hello World

Here's a simple hello world example to get your feet wet:

$ cat

open Opium

module Person = struct
  type t =
    { name : string
    ; age : int

  let yojson_of_t t = `Assoc [ "name", `String; "age", `Int t.age ]

  let t_of_yojson yojson =
    match yojson with
    | `Assoc [ ("name", `String name); ("age", `Int age) ] -> { name; age }
    | _ -> failwith "invalid person json"

let print_person_handler req =
  let name = Router.param req "name" in
  let age = Router.param req "age" |> int_of_string in
  let person = {; age } |> Person.yojson_of_t in
  Lwt.return (Response.of_json person)

let update_person_handler req =
  let open Lwt.Syntax in
  let+ json = Request.to_json_exn req in
  let person = Person.t_of_yojson json in (fun m -> m "Received person: %s";
  Response.of_json (`Assoc [ "message", `String "Person saved" ])

let streaming_handler req =
  let length = Body.length req.Request.body in
  let content = Body.to_stream req.Request.body in
  let body = String.uppercase_ascii content in
  Response.make ~body:(Body.of_stream ?length body) () |> Lwt.return

let print_param_handler req =
  Printf.sprintf "Hello, %s\n" (Router.param req "name")
  |> Response.of_plain_text
  |> Lwt.return

let _ =
  |> "/hello/stream" streaming_handler
  |> App.get "/hello/:name" print_param_handler
  |> App.get "/person/:name/:age" print_person_handler
  |> App.patch "/person" update_person_handler
  |> App.run_command

compile and run with:

$ dune exec examples/hello_world.exe &

then call

curl http://localhost:3000/person/john_doe/42

You should see the greeting



The two fundamental building blocks of opium are:

  • Handlers: Request.t -> Response.t Lwt.t
  • Middleware: Rock.Handler.t -> Rock.Handler.t

Almost all of opium's functionality is assembled through various middleware. For example: debugging, routing, serving static files, etc. Creating middleware is usually the most natural way to extend an opium app.

Here's how you'd create a simple middleware turning away everyone's favourite browser.

open Opium

module Reject_user_agent = struct
  let is_ua_msie =
    let re = Re.compile (Re.str "MSIE") in
    Re.execp re

  let m =
    let filter handler req =
      match Request.header "user-agent" req with
      | Some ua when is_ua_msie ua ->
        Response.of_plain_text ~status:`Bad_request "Please upgrade your browser"
        |> Lwt.return
      | _ -> handler req
    Rock.Middleware.create ~filter ~name:"Reject User-Agent"

let index_handler _request = Response.of_plain_text "Hello World!" |> Lwt.return

let _ =
  |> App.get "/" index_handler
  |> App.middleware Reject_user_agent.m
  |> App.cmd_name "Reject UA"
  |> App.run_command

Compile with:

$ dune build example/simple_middleware/

Here we also use the ability of Opium to generate a cmdliner term to run your app. Run your executable with --help to see the options that are available to you. For example:

# run in debug mode on port 9000
$ dune exec example/simple_middleware/main.exe -- -p 9000 -d