(** JSON API Helpers for Requests This module provides high-level combinators for working with JSON APIs, reducing boilerplate when building API clients. {2 Example Usage} {[ open Requests_json_api (* Define a Jsont codec for your type *) type user = { id : int; name : string } let user_jsont = Jsont.Object.map (fun id name -> { id; name }) |> Jsont.Object.mem "id" Jsont.int ~enc:(fun u -> u.id) |> Jsont.Object.mem "name" Jsont.string ~enc:(fun u -> u.name) |> Jsont.Object.finish let users_jsont = Jsont.list user_jsont let fetch_users session = get_json_exn session (base_url / "users") users_jsont ]} *) (** {1 JSON Request Helpers} *) val get_json_exn : Requests.t -> string -> 'a Jsont.t -> 'a (** [get_json_exn session url decoder] makes a GET request, checks status is 2xx, reads and parses JSON body using the provided Jsont decoder. Raises [Failure] on any error (HTTP, network, or JSON parse). *) val get_json : Requests.t -> string -> 'a Jsont.t -> ('a, [> `Http of int * string | `Json_error of string]) result (** Like [get_json_exn] but returns [Result] instead of raising exceptions. Returns [Ok parsed_value] on success, or [Error] with details on failure. *) val post_json : Requests.t -> string -> 'a Jsont.t -> 'a -> Requests.Response.t (** [post_json session url codec value] encodes [value] using the Jsont codec and POSTs it to the URL. Returns the raw response for custom handling. *) val post_json_exn : Requests.t -> string -> 'a Jsont.t -> 'a -> string (** Like [post_json] but checks status is 2xx and returns the response body as a string. Raises [Failure] on non-2xx status. *) val post_json_result : Requests.t -> string -> 'a Jsont.t -> 'a -> (string, int * string) result (** Like [post_json_exn] but returns [Result] instead of raising. [Ok body] on 2xx status, [Error (status, body)] otherwise. *) val post_json_decode_exn : Requests.t -> string -> req:'a Jsont.t -> 'a -> resp:'b Jsont.t -> 'b (** [post_json_decode_exn session url ~req req_value ~resp] encodes [req_value] using the [req] codec, POSTs it to the URL, checks status is 2xx, and decodes the response using the [resp] codec. Raises [Failure] on any error. *) val post_json_decode : Requests.t -> string -> req:'a Jsont.t -> 'a -> resp:'b Jsont.t -> ('b, [> `Http of int * string | `Json_error of string]) result (** Like [post_json_decode_exn] but returns [Result] instead of raising. *) val put_json_exn : Requests.t -> string -> 'a Jsont.t -> 'a -> string (** [put_json_exn session url codec value] encodes [value] and PUTs it to the URL. Returns response body. Raises [Failure] on non-2xx status. *) val put_json_decode_exn : Requests.t -> string -> req:'a Jsont.t -> 'a -> resp:'b Jsont.t -> 'b (** Like [post_json_decode_exn] but uses PUT method. *) val patch_json_exn : Requests.t -> string -> 'a Jsont.t -> 'a -> string (** [patch_json_exn session url codec value] encodes [value] and PATCHes it to the URL. Returns response body. Raises [Failure] on non-2xx status. *) val delete_json_exn : Requests.t -> string -> string (** [delete_json_exn session url] makes a DELETE request. Returns response body. Raises [Failure] on non-2xx status. *) (** {1 JSON Parsing Helpers} *) val parse_json : 'a Jsont.t -> string -> 'a (** [parse_json decoder body_str] parses a JSON string using the provided Jsont decoder. Raises exception on parse error. *) val parse_json_result : 'a Jsont.t -> string -> ('a, string) result (** Like [parse_json] but returns [Result] on parse error instead of raising. *) (** {1 Low-Level Helpers} *) val read_body : Requests.Response.t -> string (** [read_body response] reads the entire response body as a string. Equivalent to [Requests.Response.body response |> Eio.Flow.read_all] *) val get_result : Requests.t -> string -> (string, int * string) result (** [get_result session url] makes a GET request and returns the result. Returns [Ok body] on 2xx status, [Error (status, body)] otherwise. *) val check_2xx : Requests.Response.t -> (string, int * string) result (** [check_2xx response] checks if the response status is 2xx. Returns [Ok body] if status is 2xx, [Error (status, body)] otherwise. *) val check_ok : Requests.Response.t -> (string, int * string) result (** [check_ok response] checks if the response status is exactly 200. Returns [Ok body] if status is 200, [Error (status, body)] otherwise. *) (** {1 URL Helpers} *) val ( / ) : string -> string -> string (** [base_url / path] joins base URL and path, handling trailing/leading slashes. Example: ["https://api.com" / "users" / "123" = "https://api.com/users/123"] *) val make_url : string -> string -> string (** [make_url base_url path] is the function form of the [/] operator. *) (** {1 Result Composition} *) module Syntax : sig val ( let* ) : ('a, 'e) result -> ('a -> ('b, 'e) result) -> ('b, 'e) result (** Result bind operator for chaining operations *) val ( let+ ) : ('a, 'e) result -> ('a -> 'b) -> ('b, 'e) result (** Result map operator *) val ( and* ) : ('a, 'e) result -> ('b, 'e) result -> ('a * 'b, 'e) result (** Result product for parallel operations *) val ( and+ ) : ('a, 'e) result -> ('b, 'e) result -> ('a * 'b, 'e) result (** Result product for parallel operations (alias) *) end (** {1 Error Handling} *) val or_fail : ('a, [< `Http of int * string | `Json_error of string]) result -> 'a (** [or_fail result] unwraps a [Result] or raises [Failure] with an error message. Useful for converting Result-based APIs to exception-based code. *) val or_fail_with : string -> ('a, [< `Http of int * string | `Json_error of string]) result -> 'a (** Like [or_fail] but prepends a custom error prefix to the failure message. *)