I have a type level function (aka type family) in a Haskell Servant App which takes a Symbol and produces a Type (Route) i.e.
type family AppRoute (x :: Symbol) where
AppRoute x = x :> Get '[HTML] RawHtml
This is expected to be used in an API:
type ServerAPI =
Get '[HTML] RawHtml
:<|> UserAPI
:<|> AdminAPI
:<|> AppRoute "about"
:<|> AppRoute "contact"
:<|> AppRoute "services"
:<|> AppRoute "blog"
:<|> AppRoute "products"
The corresponding server function is
server :: Server ServerAPI
server =
html
:<|> userServer
:<|> adminServer
:<|> html
:<|> html
:<|> html
:<|> html
:<|> html
Essentially all the AppRoutes go to the same endpoint (raw html file). Is there a way to eliminate the duplication (referring to the last five routes) by writing something like (this does not compile)
type family AppRoute where
AppRoute = String :> Get '[HTML] RawHtml
type ServerAPI =
Get '[HTML] RawHtml
:<|> UserAPI
:<|> AdminAPI
:<|> AppRoute _ -- * the problem is here. One function is needed here
with a corresponding server
server :: Server ServerAPI
server =
html
:<|> userServer
:<|> adminServer
:<|> html
So in effect, AppRoutes is a type level function that takes any string and returns a route.
In summary, instead of writing
:<|> AppRoute "about" :<|> AppRoute "contact" :<|> AppRoute "services" :<|> AppRoute "products"
I want to be able to write just :<|> AppRoute _
You may use Capture
to capture any path. However, it will need to be preceded by :
char. So for example
type AppRoute = Capture "routePath" String :> Get '[HTML] RawHtml
type ServerAPI =
Get '[HTML] RawHtml
:<|> UserAPI
:<|> AdminAPI
:<|> AppRoute
Now, AppRoute
will trigger on yourserver.com/:thisIsMyPath/
and pass "thisIsMyPath"
as an argument for the endpoint. I have currently no idea how to bypass this :
. Assuming that html
is an endpoint that won't depend on given path at this moment, you may define your whole server as
server :: Server ServerAPI
server = html
:<|> userServer
:<|> adminServer
:<|> const html
You may read about it here.
Btw, why not use type
alias instead of taking tanky type families? In my Servant apps I usually do
type AppRoute (x :: Symbol) = x :> Get '[HTML] RawHtml
Which works perfectly.