Brady Ouren

Upgrading Servant Clients

A few things have changed with Servant’s client deriving between 0.4 and 0.7. I’ll document here what I did to upgrade.

Follow the types

First thing you’ll notice after building your project with the 0.7 version of servant client is some failing functions. Let’s follow the types and do what they tell us.

The first error is that BaseUrl takes an extra “path” argument. It’s just a string; ghc tells us this.

makeBaseUrl = do
  h <- Config.domain <$> Config.remoteConfig
  p <- Config.port <$> Config.remoteConfig
  -- BaseUrl has 1 extra argument
  -- used to be: pure $ BaseUrl Http h p
  pure $ BaseUrl Http h p ""

Now we use ExceptT instead of EitherT. It’s better, and ghc tells us that’s what it’s expecting.

type Action a = ExceptT ServantError IO a

This type alias has existed as ClientM and Handler (see this and this) but I prefer to write this explicitly and forego all the changes that might happen in servant.

The next compiler error says something about an expected Manager argument to one of our bound client functions.

Http manager

There is a new dependency on http-client. Luckily I had structured my code to use this generic run function, so I only had to make manager updates here.

run action = do
  baseUrl <- makeBaseUrl
  manager <- newManager defaultManagerSettings
  result <- runExceptT $ action manager baseUrl
  case result of
    Left message -> error (show message)
    Right x -> pure x

Essentially we only needed to pass 2 new arguments to the action (the derived functions from client) and run them inside runExceptT.

The last compiler error you’ll get is an extra argument to client which is just a remnant you can delete. It was previously the BaseUrl argument, but we’ve already moved that to the run function.

listDocuments' :<|> createDocument' =
  -- we no longer pass the url into the `client` deriving function.
  client documentAPI

That’s pretty much it. I didn’t really have to look anything up. The compiler didn’t directly tell me how to fix it, but it did tell me which parts of my code had strange arguments, which were then easy to correct.

Matter of fact, the commit where I recently did this is here if that’s your sort of thing.