servant-gdp

Servant (A web api framework) and GDP (Ghosts of Departed Proofs) is here combined to allow for quite expressive API declarations. This is achieved by parsing captured API-input as named variables, making the named contexts span the entire request. This in turn, makes it possible to express a lot of domain knowledge or requirements in the Servant API type. For an example of how this can look like check out https://github.com/mtonnberg/gdp-demo


Keywords
lib, library, Propose Tags , Servant.GDP, Servant, GDP, knowledge captured, See a full example, https://github.com/mtonnberg/gdp-demo, Knowledge-as-Code
License
BSD-3-Clause
Install
cabal install servant-gdp-0.0.1.2

Documentation

Servant ❤️ GDP

In a nutshell

To get more productive, produce high-quality web APIs and reduce the number of needed tests we can combine Servant (A web api framework) and GDP (Ghosts of Departed Proofs). This allows for quite expressive API declarations which leads to more knowledge captured. See a full example.

-- http://localhost/div/10/5 would return 2
type DivWebApi numerator denominator =
  "div"
    :> CaptureNamed (Int ~~ numerator 
                    ::: IsPositive numerator)
    :> CaptureNamed (Int ~~ denominator
                    ::: IsPositive denominator)
    :> Get
         '[JSON]
         ( Int ? IsEqualOrLessThan numerator
         )

or

-- http://localhost/habitats/savanna/animals/3
-- could return ["lion", "elephant"]
type GetAnimalsForHabitat user habitat pagesize =
  AuthProtect "normalUser"
    :> "habitats"
    :> CaptureNamed (String ~~ habitat ::: IsNonEmpty habitat && IsTrimmed habitat)
    :> "animals"
    :> CaptureNamed (Int ~~ pagesize ::: IsPositive pagesize)
    :> Get
         '[JSON]
         ( ( [String ? IsAValidatedAnimal habitat] ? HasAMaximumLengthOf pagesize
           )
             ::: user `HasAccessToHabitat` habitat
         )

See https://github.com/mtonnberg/gdp-demo for a full, working example.

How does it work

API-input is captured as named variables, making the named contexts span the entire request. This in turn, makes it possible to express a lot of domain knowledge/requirements in the Servant API type.

Why does this exist?

To both make the API-capabilities clearer and to make it easier to implement.

It is often quite easy to identify domain rules, invariants and preconditions for the API but hard to capture that knowledge in the types. Instead a lot of domain rules and requirements are hidden in the implementation logic.

Moving information to the types are in line with Knowledge-as-Code, read more about the benefits here: Knowledge-as-Code