ExceptT adds computations that may terminate with an error value to an underlying monad. In a Servant handler, it converts typed failures into ServerError values while leaving IO available for business-logic effects. section 14.2.1
type AstroAPI
= ( "meteor" #1
:> ReqBody '[JSON] MeteorTemplate #2
:> Post '[JSON] MeteorId #3
)
:<|>
( «asteroid» #4
:> ReqBody '[JSON] AsteroidTemplate #5
:> Post '[JSON] AsteroidId #6
)
The Handler alias makes the transformer’s role explicit: handlers return either a Servant error or a successful result while performing IO. section 14.2.1
A database-backed handler
type Handler a = ExceptT ServerError IO a getObject :: AstroObjectId -> Handler (Maybe AstroObject) getObject objectId = do dbConn <- liftIO (DB.connect dbConfig) liftIO $ DB.query dbConn ("SELECT * FROM astro_objects WHERE id == " ++ show objectId) The handler uses liftIO for database effects while retaining Handler as its result context. section 14.2.1
ExceptT also forms the error layer inside a larger server stack: ReaderT supplies runtime configuration, ExceptT handles Servant exceptions, and IO performs network calls. hoistServer then converts the custom stack into Servant’s native environment. section 14.2.3 section 14.2.3
