My agentic slop goes here. Not intended for anyone else!
1(** JSON API Helpers for Requests
2
3 This module provides high-level combinators for working with JSON APIs,
4 reducing boilerplate when building API clients.
5
6 {2 Example Usage}
7
8 {[
9 open Requests_json_api
10
11 (* Define a Jsont codec for your type *)
12 type user = { id : int; name : string }
13
14 let user_jsont =
15 Jsont.Object.map (fun id name -> { id; name })
16 |> Jsont.Object.mem "id" Jsont.int ~enc:(fun u -> u.id)
17 |> Jsont.Object.mem "name" Jsont.string ~enc:(fun u -> u.name)
18 |> Jsont.Object.finish
19
20 let users_jsont = Jsont.list user_jsont
21
22 let fetch_users session =
23 get_json_exn session (base_url / "users") users_jsont
24 ]}
25*)
26
27(** {1 JSON Request Helpers} *)
28
29val get_json_exn : Requests.t -> string -> 'a Jsont.t -> 'a
30(** [get_json_exn session url decoder] makes a GET request, checks status is 2xx,
31 reads and parses JSON body using the provided Jsont decoder.
32 Raises [Failure] on any error (HTTP, network, or JSON parse). *)
33
34val get_json : Requests.t -> string -> 'a Jsont.t ->
35 ('a, [> `Http of int * string | `Json_error of string]) result
36(** Like [get_json_exn] but returns [Result] instead of raising exceptions.
37 Returns [Ok parsed_value] on success, or [Error] with details on failure. *)
38
39val post_json : Requests.t -> string -> 'a Jsont.t -> 'a -> Requests.Response.t
40(** [post_json session url codec value] encodes [value] using the Jsont codec and POSTs it to the URL.
41 Returns the raw response for custom handling. *)
42
43val post_json_exn : Requests.t -> string -> 'a Jsont.t -> 'a -> string
44(** Like [post_json] but checks status is 2xx and returns the response body as a string.
45 Raises [Failure] on non-2xx status. *)
46
47val post_json_result : Requests.t -> string -> 'a Jsont.t -> 'a ->
48 (string, int * string) result
49(** Like [post_json_exn] but returns [Result] instead of raising.
50 [Ok body] on 2xx status, [Error (status, body)] otherwise. *)
51
52val post_json_decode_exn : Requests.t -> string ->
53 req:'a Jsont.t -> 'a -> resp:'b Jsont.t -> 'b
54(** [post_json_decode_exn session url ~req req_value ~resp] encodes [req_value] using the [req] codec,
55 POSTs it to the URL, checks status is 2xx, and decodes the response using the [resp] codec.
56 Raises [Failure] on any error. *)
57
58val post_json_decode : Requests.t -> string ->
59 req:'a Jsont.t -> 'a -> resp:'b Jsont.t ->
60 ('b, [> `Http of int * string | `Json_error of string]) result
61(** Like [post_json_decode_exn] but returns [Result] instead of raising. *)
62
63val put_json_exn : Requests.t -> string -> 'a Jsont.t -> 'a -> string
64(** [put_json_exn session url codec value] encodes [value] and PUTs it to the URL.
65 Returns response body. Raises [Failure] on non-2xx status. *)
66
67val put_json_decode_exn : Requests.t -> string ->
68 req:'a Jsont.t -> 'a -> resp:'b Jsont.t -> 'b
69(** Like [post_json_decode_exn] but uses PUT method. *)
70
71val patch_json_exn : Requests.t -> string -> 'a Jsont.t -> 'a -> string
72(** [patch_json_exn session url codec value] encodes [value] and PATCHes it to the URL.
73 Returns response body. Raises [Failure] on non-2xx status. *)
74
75val delete_json_exn : Requests.t -> string -> string
76(** [delete_json_exn session url] makes a DELETE request.
77 Returns response body. Raises [Failure] on non-2xx status. *)
78
79(** {1 JSON Parsing Helpers} *)
80
81val parse_json : 'a Jsont.t -> string -> 'a
82(** [parse_json decoder body_str] parses a JSON string using the provided Jsont decoder.
83 Raises exception on parse error. *)
84
85val parse_json_result : 'a Jsont.t -> string -> ('a, string) result
86(** Like [parse_json] but returns [Result] on parse error instead of raising. *)
87
88(** {1 Low-Level Helpers} *)
89
90val read_body : Requests.Response.t -> string
91(** [read_body response] reads the entire response body as a string.
92 Equivalent to [Requests.Response.body response |> Eio.Flow.read_all] *)
93
94val get_result : Requests.t -> string -> (string, int * string) result
95(** [get_result session url] makes a GET request and returns the result.
96 Returns [Ok body] on 2xx status, [Error (status, body)] otherwise. *)
97
98val check_2xx : Requests.Response.t -> (string, int * string) result
99(** [check_2xx response] checks if the response status is 2xx.
100 Returns [Ok body] if status is 2xx, [Error (status, body)] otherwise. *)
101
102val check_ok : Requests.Response.t -> (string, int * string) result
103(** [check_ok response] checks if the response status is exactly 200.
104 Returns [Ok body] if status is 200, [Error (status, body)] otherwise. *)
105
106(** {1 URL Helpers} *)
107
108val ( / ) : string -> string -> string
109(** [base_url / path] joins base URL and path, handling trailing/leading slashes.
110
111 Example: ["https://api.com" / "users" / "123" = "https://api.com/users/123"] *)
112
113val make_url : string -> string -> string
114(** [make_url base_url path] is the function form of the [/] operator. *)
115
116(** {1 Result Composition} *)
117
118module Syntax : sig
119 val ( let* ) : ('a, 'e) result -> ('a -> ('b, 'e) result) -> ('b, 'e) result
120 (** Result bind operator for chaining operations *)
121
122 val ( let+ ) : ('a, 'e) result -> ('a -> 'b) -> ('b, 'e) result
123 (** Result map operator *)
124
125 val ( and* ) : ('a, 'e) result -> ('b, 'e) result -> ('a * 'b, 'e) result
126 (** Result product for parallel operations *)
127
128 val ( and+ ) : ('a, 'e) result -> ('b, 'e) result -> ('a * 'b, 'e) result
129 (** Result product for parallel operations (alias) *)
130end
131
132(** {1 Error Handling} *)
133
134val or_fail : ('a, [< `Http of int * string | `Json_error of string]) result -> 'a
135(** [or_fail result] unwraps a [Result] or raises [Failure] with an error message.
136 Useful for converting Result-based APIs to exception-based code. *)
137
138val or_fail_with : string -> ('a, [< `Http of int * string | `Json_error of string]) result -> 'a
139(** Like [or_fail] but prepends a custom error prefix to the failure message. *)