git.iko.soy/rowtype-yoga/purescript-yoga-fetch-om
on main help sign in
opt helper: partial query params without Just/Nothing boilerplate
commitc78cf656ae2b9990b1dc1e6e397080df78c5f9b7 authorMark <mark.eibes@gmail.com> dateSat 18 Apr 2026, 17:05 (5 months ago) parentc7148eb
3 files changed+53−50
src/Yoga/Fetch/Om.purs+14−20 src/Yoga/Fetch/Om/ClientFunction.purs+24−15 test/Roundtrip.Spec.purs+15−15
src/Yoga/Fetch/Om.purs+14−20
@@ -13,7 +13,6 @@ module Yoga.Fetch.Om
1313 , module Yoga.HTTP.API.Route
1414 , module Yoga.HTTP.API.Path
1515 , plainText
16− , opt
1716 , module Yoga.Fetch.Om.StreamDecode
1817 , module Yoga.Fetch.Om.Simple
1918 ) where
@@ -35,7 +34,7 @@ import Yoga.Fetch.Om.BuildUrl (class BuildUrl, buildUrl)
3534import Yoga.Fetch.Om.Simple (class DecodeResponse, decodeResponse, FetchError, FetchResponse, get, getWithHeaders, delete, deleteWithHeaders, delete_, post, postWithHeaders, post_, put, putWithHeaders, put_, patch, patchWithHeaders, patch_)
3635import Yoga.Fetch.Om.StreamDecode (class StreamDecode, decodeStream)
3736import Yoga.Fetch.Om.ClientFunction (class BuildClientFn, class CheckBodyIsUnit, buildClientFn)
38−import Yoga.Fetch.Om.QueryDefaults (class BuildQueryFromPartial, buildQueryFromPartial)
37+import Yoga.Fetch.Om.ReadOptional (class ReadPartialQuery, readPartialQuery)
3938import Yoga.Fetch.Om.ExtractParams (class ExtractRequestBody, class ExtractBodyEncoding, class ExtractRequestHeaders)
4039import Yoga.Fetch.Om.MakeRequest (class MakeRequest, class BodyEncoding, makeRequest, encodingContentType, encodeBody)
4140import Yoga.Fetch.Om.ParseResponse (class ParseResponse, parseResponse)
@@ -74,11 +73,11 @@ else instance
7473 rest = toHeaders (Proxy :: Proxy tail) (unsafeCoerce headers :: Record tailRow)
7574
7675class DeriveClientFn
77− :: forall k1. Type -> k1 -> Type -> Row Type -> Row Type -> Row Type -> Row Type -> Type -> Type -> Constraint
76+ :: forall k1. Type -> k1 -> Type -> Row Type -> Row Type -> Row Type -> Row Type -> Row Type -> Type -> Type -> Constraint
7877class
79− DeriveClientFn method segments request response ctx extraErr routeErrors result fn
78+ DeriveClientFn method segments request response ctx extraErr given routeErrors result fn
8079 | method segments request response -> routeErrors result
81− , method segments request response ctx extraErr -> fn where
80+ , method segments request response ctx extraErr given -> fn where
8281 deriveClientFn :: String -> Proxy (Route method segments request response) -> fn
8382
8483instance
@@ -98,18 +97,23 @@ instance
9897 , Row.Union pathParams queryParams pathQuery
9998 , Row.Nub pathQuery pathQuery
10099 , RowToList pathQuery pathQueryRL
100+ , RowToList queryParams queryParamsRL
101+ , ReadPartialQuery queryParamsRL queryParams
101102 , RowToList headers headersRL
102103 , ToHeaders headersRL headers
103104 , CheckBodyIsUnit body bodyFlag
104105 , Row.Union routeErrors extraErr errRow
105− , BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers { | ctx } errRow result fn
106+ , BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers given { | ctx } errRow result fn
106107 ) =>
107− DeriveClientFn method segments request response ctx extraErr routeErrors result fn where
108+ DeriveClientFn method segments request response ctx extraErr given routeErrors result fn where
108109 deriveClientFn baseUrl _ =
109− buildClientFn (Proxy :: _ pathQueryRL) (Proxy :: _ headersRL) (Proxy :: _ bodyFlag) impl
110+ buildClientFn (Proxy :: _ pathQueryRL) (Proxy :: _ headersRL) (Proxy :: _ bodyFlag) convert impl
110111 where
112+ convert :: Record given -> Record pathQuery
113+ convert = unsafeCoerce
111114 impl :: Record pathQuery -> Record headers -> body -> Om { | ctx } errRow result
112115 impl pathQueryRec headersRec bodyVal = widenOm do
116+ let queryParamsRec = readPartialQuery @queryParamsRL pathQueryRec
113117 let url = buildUrl baseUrl (Proxy :: _ segments) pathParamsRec queryParamsRec
114118 let hdrs = toHeaders (Proxy :: _ headersRL) headersRec
115119 let ct = encodingContentType (Proxy :: _ encoding)
@@ -118,7 +122,6 @@ instance
118122 variantOrValue (Proxy :: _ successRL) variant # pure
119123 where
120124 pathParamsRec = unsafeCoerce pathQueryRec :: Record pathParams
121− queryParamsRec = unsafeCoerce pathQueryRec :: Record queryParams
122125 widenOm :: Om (Record ()) routeErrors ~> Om { | ctx } errRow
123126 widenOm = unsafeCoerce
124127
@@ -146,15 +149,6 @@ deriveClient baseUrl = deriveClientImpl @ctx @extraErr baseUrl (Proxy :: _ { | r
146149plainText :: PlainText -> String
147150plainText = unsafeCoerce
148151
149−-- | Convert a partial record to a full query params record.
150−-- | Provided fields get `Just`, missing fields get `Nothing`.
151−-- |
152−-- | ```purescript
153−-- | api.listUsers $ opt { limit: 10 }
154−-- | -- instead of: api.listUsers { limit: Just 10, offset: Nothing }
155−-- | ```
156−opt :: forall @queryParams provided. BuildQueryFromPartial queryParams provided => Record provided -> Record queryParams
157−opt = buildQueryFromPartial
158152
159153instance
160154 ( RowToList routesRow rl
@@ -172,7 +166,7 @@ instance DeriveClientRL ctx extraErr RL.Nil acc acc where
172166
173167instance
174168 ( IsSymbol label
175− , DeriveClientFn method segments request response ctx extraErr routeErrors result fn
169+ , DeriveClientFn method segments request response ctx extraErr given routeErrors result fn
176170 , DeriveClientRL ctx extraErr tail acc1 acc2
177171 , Row.Cons label fn acc2 out
178172 , Row.Lacks label acc2
@@ -181,5 +175,5 @@ instance
181175 DeriveClientRL ctx extraErr (RL.Cons label (Route method segments request response) tail) acc1 out where
182176 deriveClientRL baseUrl _ acc = Record.insert (Proxy :: _ label) clientFn rest
183177 where
184− clientFn = deriveClientFn @_ @_ @_ @_ @ctx @extraErr baseUrl (Proxy :: _ (Route method segments request response))
178+ clientFn = deriveClientFn @_ @_ @_ @_ @ctx @extraErr @given baseUrl (Proxy :: _ (Route method segments request response))
185179 rest = deriveClientRL @ctx @extraErr baseUrl (Proxy :: _ tail) acc
src/Yoga/Fetch/Om/ClientFunction.purs+24−15
@@ -22,16 +22,17 @@ instance CheckBodyIsUnit Unit IsUnit
2222else instance CheckBodyIsUnit body IsNotUnit
2323
2424class BuildClientFn
25− :: RowList Type -> RowList Type -> Type -> Type -> Row Type -> Row Type -> Type -> Row Type -> Type -> Type -> Constraint
25+ :: RowList Type -> RowList Type -> Type -> Type -> Row Type -> Row Type -> Row Type -> Type -> Row Type -> Type -> Type -> Constraint
2626class
27− BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers ctx errorRow result fn
27+ BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers given ctx errorRow result fn
2828 | pathQueryRL -> pathQuery
2929 , headersRL -> headers
30− , pathQueryRL headersRL bodyFlag body ctx errorRow result -> fn where
30+ , pathQueryRL headersRL bodyFlag body given ctx errorRow result -> fn where
3131 buildClientFn
3232 :: Proxy pathQueryRL
3333 -> Proxy headersRL
3434 -> Proxy bodyFlag
35+ -> (Record given -> Record pathQuery)
3536 -> (Record pathQuery -> Record headers -> body -> Om ctx errorRow result)
3637 -> fn
3738
@@ -43,11 +44,12 @@ instance
4344 Unit
4445 ()
4546 ()
47+ given
4648 ctx
4749 errorRow
4850 result
4951 (Om ctx errorRow result) where
50− buildClientFn _ _ _ f = f {} {} unit
52+ buildClientFn _ _ _ _ f = f {} {} unit
5153
5254-- No path/query, no headers, with body
5355instance
@@ -57,11 +59,12 @@ instance
5759 body
5860 ()
5961 ()
62+ given
6063 ctx
6164 errorRow
6265 result
6366 (body -> Om ctx errorRow result) where
64− buildClientFn _ _ _ f b = f {} {} b
67+ buildClientFn _ _ _ _ f b = f {} {} b
6568
6669-- No path/query, with headers, no body
6770instance
@@ -71,11 +74,12 @@ instance
7174 Unit
7275 ()
7376 h
77+ given
7478 ctx
7579 errorRow
7680 result
7781 (Record h -> Om ctx errorRow result) where
78− buildClientFn _ _ _ f hdrs = f {} hdrs unit
82+ buildClientFn _ _ _ _ f hdrs = f {} hdrs unit
7983
8084-- No path/query, with headers, with body
8185instance
@@ -85,11 +89,12 @@ instance
8589 body
8690 ()
8791 h
92+ given
8893 ctx
8994 errorRow
9095 result
9196 (Record h -> body -> Om ctx errorRow result) where
92− buildClientFn _ _ _ f hdrs b = f {} hdrs b
97+ buildClientFn _ _ _ _ f hdrs b = f {} hdrs b
9398
9499-- With path/query, no headers, no body
95100instance
@@ -99,11 +104,12 @@ instance
99104 Unit
100105 pq
101106 ()
107+ given
102108 ctx
103109 errorRow
104110 result
105− (Record pq -> Om ctx errorRow result) where
106− buildClientFn _ _ _ f pqr = f pqr {} unit
111+ (Record given -> Om ctx errorRow result) where
112+ buildClientFn _ _ _ convert f pqr = f (convert pqr) {} unit
107113
108114-- With path/query, no headers, with body
109115instance
@@ -113,11 +119,12 @@ instance
113119 body
114120 pq
115121 ()
122+ given
116123 ctx
117124 errorRow
118125 result
119− (Record pq -> body -> Om ctx errorRow result) where
120− buildClientFn _ _ _ f pqr b = f pqr {} b
126+ (Record given -> body -> Om ctx errorRow result) where
127+ buildClientFn _ _ _ convert f pqr b = f (convert pqr) {} b
121128
122129-- With path/query, with headers, no body
123130instance
@@ -127,11 +134,12 @@ instance
127134 Unit
128135 pq
129136 h
137+ given
130138 ctx
131139 errorRow
132140 result
133− (Record pq -> Record h -> Om ctx errorRow result) where
134− buildClientFn _ _ _ f pqr hdrs = f pqr hdrs unit
141+ (Record given -> Record h -> Om ctx errorRow result) where
142+ buildClientFn _ _ _ convert f pqr hdrs = f (convert pqr) hdrs unit
135143
136144-- With path/query, with headers, with body
137145instance
@@ -141,8 +149,9 @@ instance
141149 body
142150 pq
143151 h
152+ given
144153 ctx
145154 errorRow
146155 result
147− (Record pq -> Record h -> body -> Om ctx errorRow result) where
148− buildClientFn _ _ _ f = f
156+ (Record given -> Record h -> body -> Om ctx errorRow result) where
157+ buildClientFn _ _ _ convert f pqr hdrs b = f (convert pqr) hdrs b
test/Roundtrip.Spec.purs+15−15
@@ -12,7 +12,7 @@ import Yoga.Fastify.Fastify as F
1212import Yoga.Fastify.Fastify (Port(..), Host(..))
1313import Yoga.Fastify.Om.API (registerAPI)
1414import Yoga.Fastify.Om.Route (Handler, handle, respond, reject, BearerToken(..))
15−import Yoga.Fetch.Om (GET, POST, PUT, DELETE, Route, JSON, type (/), type (:), type (:?), client, opt)
15+import Yoga.Fetch.Om (GET, POST, PUT, DELETE, Route, JSON, type (/), type (:), type (:?), client)
1616import Yoga.Om (Om, ask, handleErrors, runOm)
1717
1818-- Types
@@ -168,12 +168,12 @@ spec = around withServer $ describe "server ↔ client roundtrip" do
168168 describe "GET with query params" do
169169 it "passes query params correctly" \_ ->
170170 runOm {} { exception: \_ -> pure unit } do
171− users <- api.listUsers { limit: Just 1, offset: Nothing }
171+ users <- api.listUsers { limit: 1 }
172172 liftAff $ (map _.name users) `shouldEqual` ["Alice"]
173173
174174 it "handles no query params" \_ ->
175175 runOm {} { exception: \_ -> pure unit } do
176− users <- api.listUsers { limit: Nothing, offset: Nothing }
176+ users <- api.listUsers {}
177177 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob", "Charlie"]
178178
179179 describe "POST with JSON body" do
@@ -230,37 +230,37 @@ spec = around withServer $ describe "server ↔ client roundtrip" do
230230 describe "GET with Boolean and Number query params" do
231231 it "passes Boolean and Number query params" \_ ->
232232 runOm {} { exception: \_ -> pure unit } do
233− result <- api.search { active: Just true, minScore: Just 3.14 }
233+ result <- api.search { active: true, minScore: 3.14 }
234234 liftAff do
235235 result.active `shouldEqual` true
236236 result.minScore `shouldEqual` 3.14
237237
238− it "handles Nothing for optional Boolean/Number" \_ ->
238+ it "handles missing optional query params" \_ ->
239239 runOm {} { exception: \_ -> pure unit } do
240− result <- api.search { active: Nothing, minScore: Nothing }
240+ result <- api.search {}
241241 liftAff do
242242 result.active `shouldEqual` false
243243 result.minScore `shouldEqual` 0.0
244244
245− describe "opt helper for partial query params" do
246− it "passes subset of query params via opt" \_ ->
245+ describe "partial query params — no wrapper needed" do
246+ it "passes subset of query params directly" \_ ->
247247 runOm {} { exception: \_ -> pure unit } do
248− users <- api.listUsers $ opt { limit: 1 }
248+ users <- api.listUsers { limit: 1 }
249249 liftAff $ (map _.name users) `shouldEqual` ["Alice"]
250250
251− it "passes all query params via opt" \_ ->
251+ it "passes all query params directly" \_ ->
252252 runOm {} { exception: \_ -> pure unit } do
253− users <- api.listUsers $ opt { limit: 2, offset: 0 }
253+ users <- api.listUsers { limit: 2, offset: 0 }
254254 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob"]
255255
256− it "passes empty record via opt" \_ ->
256+ it "passes empty record for all defaults" \_ ->
257257 runOm {} { exception: \_ -> pure unit } do
258− users <- api.listUsers $ opt {}
258+ users <- api.listUsers {}
259259 liftAff $ (map _.name users) `shouldEqual` ["Alice", "Bob", "Charlie"]
260260
261− it "passes Boolean via opt" \_ ->
261+ it "passes partial Boolean query param" \_ ->
262262 runOm {} { exception: \_ -> pure unit } do
263− result <- api.search $ opt { active: true }
263+ result <- api.search { active: true }
264264 liftAff do
265265 result.active `shouldEqual` true
266266 result.minScore `shouldEqual` 0.0