type family instance for field types in servant’s NamedRoutes

  Kiến thức lập trình

I’m looking for a way to decorate a servant API definition: Whenever an endpoint returns a list of things, I’d like to add a query parameter.
So far, I have a type family that works on APIs that are defined with the traditional approach using :<|>:

type family AddLimit a :: Type where
  AddLimit ((sym :: Symbol) :> rest) = sym :> AddLimit rest
  AddLimit (first :> rest) = first :> AddLimit rest
  AddLimit (left :<|> right) = AddLimit left :<|> AddLimit right
  AddLimit (Get contentTypes [res]) = QueryParam "limit" Int :> Get contentTypes [res]
  AddLimit (Get contentTypes res) = Get contentTypes res

(this would require more instances to be complete, but it’s enough to show the idea)

That will work if my API looks like this:

type API =
  "foo" :> Get '[JSON] Int
    :<|> "bar" :> Get '[JSON] [Int]

But it won’t work when using NamedRoutes:

data MyNamedAPI mode = MyNamedAPI
  { foo :: mode :- "foo" :> Get '[JSON] Int,
    bar :: mode :- "bar" :> Get '[JSON] [Int]
  }
  deriving stock (Generic)

I can add the following instance to the type family:

AddLimit (NamedRoutes nr) = AddLimit (ToServantApi nr)

but that will turn my API definition in the equivalent representation using :<|>.

Alternatively, I could wrap AddLimit around all fields manually:

data MyNamedAPI mode = MyNamedAPI
  { foo :: mode :- AddLimit ("foo" :> Get '[JSON] Int),
    bar :: mode :- AddLimit ("bar" :> Get '[JSON] [Int])
  }
  deriving stock (Generic)

Is there a way to use the type family to do that with less duplication? There is ToServant in servant-generic, but the inverse is only supported on the value level using fromServant

2

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT