| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Internal.Data.Basic.Tutorial
Description
This tutorial describes how to use the basic library. Usually you would use the functions provided in the Data.Basic.TH module Internal.to generate all of the declarations in this tutorial from your database schema.
Basic is a database-first library meaning the schema comes from the database instead of your code. The library provides mechanisms for "explaining" your schema to the compiler. It can then use this information to provide a typesafe and convenient access and control of your data.
We start by defining a data type.
data User = User { _serId :: Key
, _serName :: Text } deriving (Eq, Ord, Read, Show)
Most of the functionality is implemented through lenses so we need to generate them for our datatype.
makeLenses ''User
Next we provide a set of instances for our type. These describe how our type maps to a database
table. We use type level strings to represent database names of the fields. Instances are
needed for each field and for each constraint on the table. We also need a FromRow instance
so the type can actually be deserialized from the query result.
instanceTableUser where -- the database name for this table typeTableNameUser = "blog_user" -- a type level list of all the fields in this table typeTableFieldsUser = ["id", "name"] -- a type level list of constraints on this table; each of these will need a corresponding -- instance that provides additional info typeTableConstraintsUser = '[ 'Unique"blog_user_pkey"] -- the table can optionally have a primary key; for this we use a type levelMaybevalue typeTablePrimaryKeyUser = 'Just"blog_user_pkey" -- a type level list of fields that are eitherRequiredorDynamicDefaulttypeTableRequiredFieldsUser = ['Required"id", 'Required"name"] -- a default user -- don't worry about undefined values, the types will make sure you can't accidentally evaluate -- themnewEntity=Entity(Userundefinedundefined) instanceUniqueConstraint"blog_user_pkey" where -- the table which this constraint targets typeUniqueTable"blog_user_pkey" = User -- you can have multiple fields that make up one unique constraint typeUniqueFields"blog_user_pkey" = '["id"] --PrimaryKeyConstraintis really just a synonym for a unique constraint + the condition that -- all the values must not be null instancePrimaryKeyConstraint"blog_user_pkey" -- each field gets an instance saying what Haskell type it maps to and providing a lens instanceTableFieldUser "id" where typeTableFieldTypeUser "id" =KeytableFieldLens= serId instanceTableFieldUser "name" where typeTableFieldTypeUser "name" =TexttableFieldLens= serName instanceFromRowUser wherefromRow= User<$>field<*>field
Now we do the same for a "blog_post" table.
@
data Post = Post { _ostId :: Key
, _ostName :: Text
, _ostUserId :: Key } deriving (Eq, Ord, Read, Show)
instance Table Post where
type TableName Post = "blog_post"
type TableFields Post = ["id", "name", "author"]
type TableConstraints Post = '[ 'ForeignKey "blog_post_author_fkey"]
type TablePrimaryKey Post = 'Just "blog_post_pkey"
type TableRequiredFields Post = ['Required "id", 'Required "name", 'Required "author"]
newEntity = Entity (Post undefined undefined)