I'm serving an API using Servant, all managed by Snap. In Servant, it's easy to include an arbitrary data type as part of a POST request, assuming it has a FromJSON
instance. For instance, I might have the following endpoint:
ReqBody '[JSON] RequestData :> Post '[JSON] [ResponseData]
How do I do the same for GET requests? From what I understand, I'd need to use the Query Parameters, but my request data consists of complex datatypes (lists, nested dictionaries) that don't seem to be readable easily, e.g. QueryParam "vals" [Int] :> Post '[JSON] [Int]
results in the error No instance for (FromHttpApiData [Int])
A workaround would be to use POST requests, which have easily readable request bodies. However, this would clash with my caching scheme in Nginx, since responses to POST requests aren't that easily cachable. Even if I can cache them, I don't want to cache all post requests, so it'd be a messy approach.
Thanks for any help!
A simple solution if you want the same level of automatic derivation as for JSON post bodies is to just send the query params as JSON
import Data.Aeson
import Servant.API
import qualified Data.Text as T
import Data.Text.Encoding
import qualified Data.ByteString.Lazy as LBS
newtype JSONEncoded a = JSONEncoded { unJSONEncoded :: a }
deriving (Eq, Show)
instance (FromJSON a) => FromHttpApiData (JSONEncoded a) where
parseQueryParam x = case eitherDecode $ LBS.fromStrict $ encodeUtf8 x of
Left err -> Left (T.pack err)
Right val -> Right (JSONEncoded val)
instance (ToJSON a) => ToHttpApiData (JSONEncoded a) where
toQueryParam (JSONEncoded x) = decodeUtf8 $ LBS.toStrict $ encode x