servant-util-beam-pg: Implementation of servant-util primitives for beam-postgres.

[ database, library, mpl, program, servant, web ] [ Propose Tags ]

[Skip to Readme]
Versions [RSS] [faq] 0.1.0, 0.1.0.1, 0.1.0.2
Change log CHANGES.md
Dependencies base (>=4.7 && <5), beam-core, beam-postgres, containers, servant, servant-client, servant-client-core, servant-server, servant-util (>=0.1.0 && <0.2), text, universum [details]
License MPL-2.0
Copyright 2019-2021 Serokell OÜ
Author Serokell
Maintainer hi@serokell.io
Category Servant, Web, Database
Home page https://github.com/serokell/servant-util#readme
Bug tracker https://github.com/serokell/servant-util/issues
Source repo head: git clone https://github.com/serokell/servant-util
Uploaded by martoon at 2021-07-26T14:00:43Z
Distributions NixOS:0.1.0.2
Executables servant-util-beam-pg-examples
Downloads 119 total (3 in the last 30 days)
Rating 1.25 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2021-07-28 [all 1 reports]

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

For package maintainers and hackage trustees

Candidates


Readme for servant-util-beam-pg-0.1.0.2

[back to package description]

Servant-util-beam-pg

This package contains backend implementation for API combinators of servant-util package via 'beam-postgres'.

Build Instructions

Run stack build servant-util-beam-pg to build everything.

Usage

Sorting

We will demonstrate this on the previously shown example:


type instance SortingParamBaseOf Book =
    ["name" ?: Isbn, "author" ?: Text]
type instance SortingParamProvidedOf Book =
    ["isbn" ?: Asc Isbn]

type GetBooks
    :> SortingParamsOf Book
    =  Get '[JSON] [Book]

Let's assume that Book definition is extended to a Beam table (this is not necessary, but simplifies our example):

data BookT f = Book
    { isbn :: C f Isbn
    , bookName :: C f Text
    , author :: C f Text
    } deriving (Generic)

type Book = BookT Identity

Implementation will look like


import Servant.Util (SortingSpecOf, HNil, (.*.))
import Servant.Util.Beam.Postgres (fieldSort)

-- some code

getBooks :: _ => SortingSpecOf Book -> m [Book]
getBooks sorting =
    runSelect . select $
        sortBy_ sorting sortingApp $
        all_ (books booksSchema)
  where
    sortingApp Book{..} =
        fieldSort @"name" bookName .*.
        fieldSort @"author" author .*.
        fieldSort @"isbn" isbn .*.
        HNil

Function sortingApp specifies how to correlate user-provided specification with fields of our table. For each field which we allow to sort on, we specify a Beam field from the table.

If one of the fields lacks such specification in sortingApp definition or order of fields is incorrect then compile error is raised. The same happens when field types in API and schema definition mismatch.

Annotating fieldSort calls with a field name is fully optional but may save you in case when several fields of the same type participate in sorting; this also improves readability.

Filtering

Filtering is implemented pretty much like sorting.

Let's consider the previously shown example:


type instance SortingParamTypesOf Book =
    ["isbn" ?: Word64, "name" ?: Text, "hasLongName" ?: Bool]

type GetBooks
    :> SortingParamsOf Book
    =  Get '[JSON] [Book]

Implementation can look like


import Servant.Util (FilteringSpecOf, HNil, (.*.))
import Servant.Util.Beam.Postgres (filterOn, manualFilter, matches_)

-- some code

getBooks :: _ => FilteringSpecOf Book -> m [Book]
getBooks filters =
    runSelect . select $ do
        book <- all_ (books booksSchema)
        guard_ $ matches_ filters (filteringApp book)
        return book
  where
    filteringApp Book{..} =
        filterOn @"isbn" isbn .*.
        filterOn @"name" bookName .*.
        manualFilter @"hasLongName"
            (\hasLongName -> hasLongName ==. (charLength_ bookName >=. 10)) .*.
        HNil

Again, for each possible user-provided filter you have to provide an implementation. Automatic filters require only a corresponding field of response, they will apply the required operation to it themselves. On the other hand, manual filters carry the value that user provides, and you implement the predicate on this value.

Again, type annotations in filterOn and manualFilter calls are optional.

Pagination

Pagination can be applied simply with paginate_ function.

For Contributors

Please see CONTRIBUTING.md for more information.

About Serokell

Servant-util is maintained and funded with ❤️ by Serokell. The names and logo for Serokell are trademark of Serokell OÜ.

We love open source software! See our other projects or hire us to design, develop and grow your idea!