{"id":16613127,"url":"https://github.com/c-cube/ocaml-twirp","last_synced_at":"2025-10-29T18:31:01.085Z","repository":{"id":209096114,"uuid":"723227720","full_name":"c-cube/ocaml-twirp","owner":"c-cube","description":"OCaml implementation of Twirp using ocaml-protoc","archived":false,"fork":false,"pushed_at":"2024-12-16T02:10:50.000Z","size":7355,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-07T21:35:35.460Z","etag":null,"topics":["http1-1","ocaml","protobuf","rpc","twirp"],"latest_commit_sha":null,"homepage":"https://c-cube.github.io/ocaml-twirp/","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/c-cube.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-25T02:08:26.000Z","updated_at":"2024-12-16T02:04:01.000Z","dependencies_parsed_at":"2024-06-25T17:30:02.530Z","dependency_job_id":"23a07a6f-077b-49c3-be04-0827a564ea97","html_url":"https://github.com/c-cube/ocaml-twirp","commit_stats":null,"previous_names":["c-cube/ocaml-twirp"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/c-cube%2Focaml-twirp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/c-cube%2Focaml-twirp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/c-cube%2Focaml-twirp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/c-cube%2Focaml-twirp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/c-cube","download_url":"https://codeload.github.com/c-cube/ocaml-twirp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238864401,"owners_count":19543533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http1-1","ocaml","protobuf","rpc","twirp"],"created_at":"2024-10-12T01:46:04.437Z","updated_at":"2025-10-29T18:30:55.652Z","avatar_url":"https://github.com/c-cube.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Twirp\n\n[![Build and Test](https://github.com/c-cube/ocaml-twirp/actions/workflows/main.yml/badge.svg)](https://github.com/c-cube/ocaml-twirp/actions/workflows/main.yml)\n\nThis is an OCaml implementation of [Twirp](https://twitchtv.github.io/twirp/)\nthat relies on [ocaml-protoc](https://github.com/mransan/ocaml-protoc/) (version 3 or above) to\ncompile protobuf IDL files.\n\n\n## License\n\nMIT license\n\n## Usage\n\nIn the following examples we use a basic \"calculator\" service\nas an example:\n\n```proto\nsyntax = \"proto3\";\n\n// single int\nmessage I32 {\n  int32 value = 0;\n}\n\n// add two numbers\nmessage AddReq {\n  int32 a = 1;\n  int32 b = 2;\n}\n\n// add an array of numbers\nmessage AddAllReq {\n  repeated int32 ints = 1;\n}\n\nservice Calculator {\n  rpc add(AddReq) returns (I32);\n\n  rpc add_all(AddAllReq) returns (I32);\n}\n```\n\nWe assume there's a dune rule to extract it into a pair\nof .ml and .mli files:\n\n```scheme\n(rule\n (targets calculator.ml calculator.mli)\n (deps calculator.proto)\n (action\n  (run ocaml-protoc --binary --pp --yojson --services --ml_out ./ %{deps})))\n```\n\n### Using Tiny_httpd as a server\n\nThe library `twirp_tiny_httpd` uses [Tiny_httpd](https://github.com/c-cube/tiny_httpd)\nas a HTTP server to host services over HTTP 1.1.\n\nTiny_httpd is a convenient little HTTP server with no dependencies\nthat uses direct style control flow\nand system threads, rather than an event loop.\nRealistically, it is sufficient\nfor low traffic services (say, less than 100 req/s), and is best used coupled\nwith a thread pool such as [Moonpool](https://github.com/c-cube/moonpool/)\nto improve efficiency.\n\n\u003cdetails\u003e\n\u003csummary\u003edetailed example\u003c/summary\u003e\n\nSee 'examples/twirp_tiny_httpd/' for an example:\n\n```ocaml\nmodule H = Tiny_httpd\nopen Calculator\n\n(* here we give concrete implementations for each of the\n  methods of the service.  *)\nmodule Service_impl = struct\n  let add (a : add_req) : i32 = default_i32 ~value:Int32.(add a.a a.b) ()\n\n  let add_all (a : add_all_req) : i32 =\n    let l = ref 0l in\n    List.iter (fun x -\u003e l := Int32.add !l x) a.ints;\n    default_i32 ~value:!l ()\nend\n\n(* instantiate the code-generated [Calculator] service\n  to turn it into a [Pbrt_services.Server.t] abstract service. *)\nlet calc_service : Twirp_tiny_httpd.handler Pbrt_services.Server.t =\n  Calculator.Server.make\n    ~add:(fun rpc -\u003e Twirp_tiny_httpd.mk_handler rpc Service_impl.add)\n    ~add_all:(fun rpc -\u003e Twirp_tiny_httpd.mk_handler rpc Service_impl.add_all)\n    ()\n\nlet () =\n  let port = try int_of_string (Sys.getenv \"PORT\") with _ -\u003e 8080 in\n  Printf.printf \"listen on http://localhost:%d/\\n%!\" port;\n\n  (* create the httpd on the given port *)\n  let server = H.create ~port () in\n  (* register the service in the httpd (adds routes) *)\n  Twirp_tiny_httpd.add_service ~prefix:(Some \"twirp\") server calc_service;\n\n  H.run_exn server\n```\n\nWe implement the concrete service `Calculator`, then turn it into\na `Pbrt_services.Server.t` (which is an abtract representation of\nany service: a set of endpoints). We can then create a [Tiny_httpd.Server.t]\n(a web server) and register the service (or multiple services) in it.\nThis will add new routes (e.g. \"/twirp/foo.bar.Calculator/add\")\nand call the functions we defined above to serve these routes.\n\n\u003c/details\u003e\n\n### Using ezcurl as a client\n\nThe library `twirp_ezcurl` uses [Ezcurl](https://github.com/c-cube/ezcurl)\nas a [Curl](https://curl.haxx.se/) wrapper to provide Twirp clients.\n\nCurl is very widely available and is a robust HTTP client; ezcurl adds a\nsimple OCaml API on top.\nTwirp_ezcurl is best used for low-traffic querying of services.\n\n\u003cdetails\u003e\n\u003csummary\u003efull example\u003c/summary\u003e\nExample (as in 'examples/twirp_ezcurl/') that computes `31 + 100`\nremotely:\n\n```ocaml\nlet spf = Printf.sprintf\n\nlet () =\n  let port = try int_of_string (Sys.getenv \"PORT\") with _ -\u003e 8080 in\n  Printf.printf \"query on http://localhost:%d/\\n%!\" port;\n\n  let r =\n    match\n      (* call [Calculator.add] with arguments [{a=31; b=100}] *)\n      Twirp_ezcurl.call ~use_tls:false ~host:\"localhost\" ~port\n        Calculator.Calculator.Client.add\n      @@ Calculator.default_add_req ~a:31l ~b:100l ()\n    with\n    | Ok x -\u003e x.value |\u003e Int32.to_int\n    | Error err -\u003e\n      failwith (spf \"call to add failed: %s\" @@ Twirp_ezcurl.show_error err)\n  in\n\n  Printf.printf \"add call: returned %d\\n%!\" r;\n  ()\n```\n\nThe main function is `Twirp_ezcurl.call`, which takes a remote host, port, service\nendpoint (code-generated from a `.proto` file), and an argument, and performs\na HTTP query.\nThe user can provide an already existing Curl client to reuse, turn TLS off or on,\nand pick the wire format (JSON or binary protobuf).\n\n\u003c/details\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fc-cube%2Focaml-twirp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fc-cube%2Focaml-twirp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fc-cube%2Focaml-twirp/lists"}