package opium
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=326b91866de90baf535f8b7d4b2ff23e39d952e573c04b3c13f1054b59ff2fb6
sha512=59b83e7c8fe5f7ae328fb7f2343fe5b8fb735e8f6ee263cfd6c75bb179688ef7cf2b4586b35a2231ed3f3c1ada543021b7a4759326ae095eb77a5f38b9fa3a8a
Description
Opium is a web framework for OCaml that provides everything you need to build safe, fast and extensible web applications.
Published: 11 Dec 2020
README
Opium
The current master branch is a WIP port to httpaf. If you are looking for the last version published to opam (that was using Cohttp), please take a look at https://github.com/rgrinberg/opium/tree/0.18.0
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
.
Installation
Stable
The latest stable version is available on opam
$ opam install opium
Master
$ opam pin add rock.~dev https://github.com/rgrinberg/opium.git
$ opam pin add opium.~dev https://github.com/rgrinberg/opium.git
Documentation
For the API documentation:
Build and view the docs for version installed locally using
odig
:odig doc opium
.
The following tutorials walk through various usecases of Opium:
A Lightweight OCaml Webapp Tutorial covers a simple webapp generating dynamic HTML on the backend and interfacing with PostgreSQL.
For examples of idiomatic usage, see the ./examples directory and the simple examples below.
Examples
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 hello_world.ml
open Opium
module Person = struct
type t =
{ name : string
; age : int
}
let yojson_of_t t = `Assoc [ "name", `String t.name; "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"
;;
end
let print_person_handler req =
let name = Router.param req "name" in
let age = Router.param req "age" |> int_of_string in
let person = { Person.name; 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
Logs.info (fun m -> m "Received person: %s" person.Person.name);
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 = Lwt_stream.map 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 _ =
App.empty
|> App.post "/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
{"name":"john_doe","age":42}
Middleware
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
in
Rock.Middleware.create ~filter ~name:"Reject User-Agent"
;;
end
let index_handler _request = Response.of_plain_text "Hello World!" |> Lwt.return
let _ =
App.empty
|> 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/main.ml
Here we also use the ability of Opium to generate a cmdliner term to run your app. Run your executable with -h
to see the options that are available to you. For example:
# run in debug mode on port 9000
$ dune exec dune build example/simple_middleware/main.exe -- -p 9000 -d
Dependencies (20)
-
multipart-form-data
>= "0.3.0"
-
uri
>= "2.2.0"
- re
- astring
-
base64
>= "3.1.0"
-
mirage-crypto
< "1.0.0"
-
tyxml
>= "4.3.0"
-
yojson
>= "1.6.0"
- magic-mime
- ptime
- cmdliner
-
mtime
>= "1.0.0"
- fmt
- logs
- conf-libev
- httpaf-lwt-unix
-
lwt
>= "5.3.0"
-
rock
= version
-
ocaml
>= "4.08"
-
dune
>= "2.0"
Dev Dependencies (3)
-
alcotest-lwt
with-test
-
alcotest
with-test
-
odoc
with-doc
Used by (9)
-
comby
< "1.7.0"
-
openapi
< "1.0.0"
- openapi-opium
- opium-graphql
-
opium-testing
>= "0.20.0"
-
sihl
>= "0.3.0~rc2"
-
sihl-contract
>= "0.3.0~rc1"
- sihl-type
-
sihl-web
< "0.3.0~rc1"
Conflicts (1)
-
result
< "1.5"