I am calling a Third Party API in which the number of variables I send as Query Params will be dynamic and will depend on an array of values.
For example, if my array=[value1], my API call would be
https://clientapi.com/test?userId=myuser&var1=value1
When my array=[value1,value2,value3], the API call should be as below
https://clientapi.com/test?userId=myuser&var1=value1&var2=value2&var3=value3.
One way I can think of doing this is to define a lot of vars in the API definition like
type ClientAPI =
QueryParam "userId" Text
:> QueryParam "var1" Text
:> QueryParam "var2" Text
:> QueryParam "var3" Text
:> QueryParam "var4" Text
:> QueryParam "var5" Text
:> Get '[JSON] MyType
and pass in my array values in the call as required.
Is there a better way to do this?
Note that Servant does provide a
QueryParamsendpoint where a single key with multiple values is allowed:So, if your third-party API had been designed this way, you'd could have just used:
Since you need to generate different key values, there's no built-in endpoint for this, but you can write one.
Writing an endpoint involves defining a (constructor-less) data type to represent the new endpoint:
and defining instances for its implementation on the server side (
HasServer) and/or the client side (HasClient).You only need the
HasClientinstance, and theHasClientinstances for existing endpoints are located in theServant.Client.Core.HasClientmodule ofservant-client-core. I adapted theQueryParamsinstance from version 0.19 to your problem. It involves some internals, so it may or may not work with earlier or later versions:The idea with this instance is that the
Clienttype family defines the parameter(s) needed by the endpoint. When you define a client using the endpoint:it's this type family instance that ensures the
a ~ Doublein the API'sQueryParamSeqcomponent gets translated into an[a] ~ [Double]parameter in the query functiongetIt.The
clientWithRoutemethod implements the client-side behavior for the endpoint. It's passed a couple of proxies (respectively for the base monad and the portion of the API from the current endpoint onwards), the request being constructed, and the[a]parameter. It should typically dispatch toclientWithRouteon the rest of the API, passing along a modified request as appropriate. This implementation just runs a bunch ofaddcalls to add pairs of indexed keys (e.g.,var1,var2) and associated values from the supplied list.The
hoistClientMonadis just boilerplate to support thehoistClientfunction.Anyway, the resulting full example which appears to work fine with the 0.19 versions of the servant packages follows: